如今的web app基本上都是先後端分離,以前接觸的大部分應用場景最終產品都是部署在同一個站點下,那麼隨着WebApi(Restful api)的發展先後端實現的徹底分離,前端不在後端框架的頁面基礎上開發,也就告別傳統上的Session判斷客戶端登錄用戶的狀況。OAuth已發佈好久,Asp.Net Identity也發佈好久。看了幾篇朋友寫的博客才把這幾個sample寫完,也解決了以前我對先後端徹底分離產生的一些疑惑。html
一、使用VS2015建立一個Empty WebApi項目。前端
二、使用Nuget導入核心命名空間。web
Install-Package Microsoft.AspNet.WebApi.Owin
數據庫
[assembly:OwinStartup(typeof(AspNet_Identity_Demo.Startup))] namespace AspNet_Identity_Demo { public class Startup { public void Configuration(IAppBuilder app) { HttpConfiguration config = new HttpConfiguration(); WebApiConfig.Register(config); app.UseWebApi(config); } } }
四、修改WebApiConfig。修改最後兩句代碼,主要以CamelCase命名法序列化webApi的返回結果。json
namespace AspNet_Identity_Demo { public static class WebApiConfig { public static void Register(HttpConfiguration config) { // Web API 配置和服務 // Web API 路由 config.MapHttpAttributeRoutes(); config.Routes.MapHttpRoute( name: "DefaultApi", routeTemplate: "api/{controller}/{id}", defaults: new { id = RouteParameter.Optional } ); //用json的方式返回webapi接口返回值 var jsonFormatter = config.Formatters.OfType<JsonMediaTypeFormatter>().First(); jsonFormatter.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver(); } } }
五、刪除Global.asax。添加Startup類後暫時用不到這個類。後端
六、添加Asp.Net Identity。先添加Identity類庫。api
Install-Package Microsoft.AspNet.Identity.Owin
跨域
namespace AspNet_Identity_Demo.Models { public class AuthContext:IdentityDbContext<IdentityUser> { public AuthContext() : base("AuthContext") { } } }
Web.config中增長connectionString服務器
<add name="AuthContext" connectionString="Data Source=.;User Id=sa;password=111111;Initial Catalog=AspNet_Identity;Integrated Security=SSPI;" providerName="System.Data.SqlClient" />
八、在Models文件夾中建立UserModel.csapp
public class UserModel { [Required] [Display(Name ="User Name")] public string UserName { get; set; } [Required] [DataType(DataType.Password)] [StringLength(100,ErrorMessage ="The {0} must be at least {2} characters long",MinimumLength =6)] public string Password { get; set; } [Required] [DataType(DataType.Password)] [Compare("Password",ErrorMessage ="The password and confirmpassword are not matched...")] public string ConfirmPassword { get; set; } }
九、添加Asp.Net Identity 倉儲支持類。
這裏用到了策略模式,把你實現的UserStore.cs做爲參數傳進UserManager構造函數中。
namespace AspNet_Identity_Demo.Models { public class AuthRepository : IDisposable { private AuthContext _ctx; private UserManager<IdentityUser> _userManager; public AuthRepository() { _ctx = new AuthContext(); _userManager = new UserManager<IdentityUser>(new UserStore<IdentityUser>(_ctx)); } public async Task<IdentityResult> Register(UserModel model) { IdentityUser user = new IdentityUser() { UserName = model.UserName }; IdentityResult result = await _userManager.CreateAsync(user,model.Password); return result; } public async Task<IdentityUser> FindUser(UserModel model) { IdentityUser user = await _userManager.FindAsync(model.UserName, model.Password); return user; } public async Task<IdentityUser> FindUserByName(string username) { IdentityUser user = await _userManager.FindByNameAsync(username); return user; } public void Dispose() { _ctx.Dispose(); _userManager.Dispose(); } } }
十、添加AccountController.cs
給Controller添加webapi訪問前綴,個人是apix,訪問時也就是http://localhost:8083/apix/account/register。
namespace AspNet_Identity_Demo.Controllers { [RoutePrefix("apix/Account")] public class AccountController : ApiController { private AuthRepository _authRepo; public AccountController() { _authRepo = new AuthRepository(); } [AllowAnonymous] [Route("Register")] public async Task<IHttpActionResult> Register(UserModel model) { if (!ModelState.IsValid) { return BadRequest(ModelState); } IdentityResult result = await _authRepo.Register(model); IHttpActionResult errorResult = GetError(result); if (errorResult != null) { return errorResult; } return Ok(); } private IHttpActionResult GetError(IdentityResult result) { if (result == null) { return InternalServerError(); } if (!result.Succeeded) { foreach (string err in result.Errors) { ModelState.AddModelError("", err); } if (ModelState.IsValid) { return BadRequest(); } return BadRequest(ModelState); } return null; } } }
OK,到了這一步就能夠在你的視線之上註冊用戶了,使用Postman調用接口並調用接口http://localhost:8080/apix/account/register。post方式調用。參數傳UserName、Password。 調用成功返回接口返回200.打開你的SQL Server。調用成功的話數據庫用到的幾張表都會生成。用戶表是dbo.AspNetUsers.
十一、添加一個數據訪問controller,OrdersController。
namespace AspNet_Identity_Demo.Controllers { [Authorize] [RoutePrefix("apix/orders")] public class OrdersController : ApiController { [Route] public IHttpActionResult Get() { return Ok(Order.CreateOrders()); } } public class Order { public int OrderID { get; set; } public string CustomerName { get; set; } public string ShipperCity { get; set; } public Boolean IsShipped { get; set; } public static List<Order> CreateOrders() { List<Order> OrderList = new List<Order> { new Order {OrderID = 10248, CustomerName = "Taiseer Joudeh", ShipperCity = "Amman", IsShipped = true }, new Order {OrderID = 10249, CustomerName = "Ahmad Hasan", ShipperCity = "Dubai", IsShipped = false}, new Order {OrderID = 10250,CustomerName = "Tamer Yaser", ShipperCity = "Jeddah", IsShipped = false }, new Order {OrderID = 10251,CustomerName = "Lina Majed", ShipperCity = "Abu Dhabi", IsShipped = false}, new Order {OrderID = 10252,CustomerName = "Yasmeen Rami", ShipperCity = "Kuwait", IsShipped = true} }; return OrderList; } } }
十二、添加OAuth Bearer Token支持類庫 Install-Package Microsoft.Owin.Security.OAuth
1三、回到Startup。添加建立token方法,主要涉及到了兩個類SimpleAuthorizationServerProvider、OAuthAuthorizationServerOptions。
[assembly:OwinStartup(typeof(AspNet_Identity_Demo.Startup))] namespace AspNet_Identity_Demo { public class Startup { public void Configuration(IAppBuilder app) { HttpConfiguration config = new HttpConfiguration(); ConfigAuth(app); WebApiConfig.Register(config); app.UseCors(CorsOptions.AllowAll); app.UseWebApi(config); } public void ConfigAuth(IAppBuilder app) { OAuthAuthorizationServerOptions option = new OAuthAuthorizationServerOptions() { AllowInsecureHttp=true, TokenEndpointPath=new PathString("/token"), AccessTokenExpireTimeSpan=TimeSpan.FromDays(1), Provider=new SimpleAuthorizationServerProvider() }; app.UseOAuthAuthorizationServer(option); app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions()); } } public class SimpleAuthorizationServerProvider : OAuthAuthorizationServerProvider { public override async Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context) { context.Validated(); } public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context) { context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { "*" }); using (AuthRepository _repo = new AuthRepository()) { IdentityUser user =await _repo.FindUser( new UserModel() { UserName=context.UserName,Password=context.Password}); if (user == null) { context.SetError("invalid_grant", "The username or password is incorrect"); return; } } var identity = new ClaimsIdentity(context.Options.AuthenticationType); identity.AddClaim(new Claim("sub", context.UserName)); identity.AddClaim(new Claim("role", "user")); context.Validated(identity); } } }
訪問http://localhost:8083/token http接口生成token。過時時間24小時。SimpleAuthorizationServerProvider 在該類中實現用戶驗證和口令生成。 注意這裏的ClamisIdentity。該類在命名空間:System.Security.Claims。 生成token主要是context.Validated(identity);這句代碼。
OK,如今能夠註冊用戶,也能夠生成token了。那麼如今有個問題來了,先後端徹底分離後,那麼確定要實現跨域訪問(CORS)。因此你看到重寫GrantResourceOwnerCredentials第一句就是添加Access-Control-Allow-Origin支持。
1三、添加Asp.Net WebApi Install-Package Microsoft.Owin.Cors。在Startup.cs Configuration方法中添加app.UseCors(CorsOptions.AllowAll);
1四、生成客戶端token。
1五、拿到token後,訪問數據接口。注意參數Authorization值有前綴Bearer。
總的來講Owin和Identity的設計仍是有點複雜的,約定的東西多一些。相比微軟早起的Membership則要優雅不少,原理和實現背後的細節還要多多挖掘,才能體會到其中的魅力。好比ClamisIdentity、 UserManager、UserStore。
Demo下載地址:https://yunpan.cn/c6yNPKhzpQgmx (提取碼:0575)
http://www.cnblogs.com/richieyang/p/4918819.html
http://bitoftech.net/2014/06/01/token-based-authentication-asp-net-web-api-2-owin-asp-net-identity/