上篇:
上面我们把自定义认证和授权的相关的最小基础类和要实现的接口都实现了,下面就是如何来进行认证和授权的配置。
首先我们要告诉系统,我们的用户和角色类以及实现的接口,这样系统在认证的时候就知道要调用哪些类和方法,其实就是注册一下上面定义的几个类而已,在Startup.cs文件的ConfigureServices方法添加如下代码:
1 public void ConfigureServices(IServiceCollection services) 2 { 3 4 ... 5 6 services.AddIdentity<HDUser, HDRole>(); 7 services.AddSingleton<IUserStore<HDUser>, HDUserStore<HDUser>>(); 8 services.AddSingleton<IRoleStore<HDRole>, HDRoleStore<HDRole>>(); 9 ... 10 11 12 }
第6行,注册我们定义的用户和角色类。第7行和第8行注册用户和角色操作类。
那么这三行代码有什么关系呢?
我们去看一下AddIdentity的源代码,看看里面都干了什么活:
1 public static IdentityBuilder AddIdentity<TUser, TRole>( 2 this IServiceCollection services, 3 Action<IdentityOptions> setupAction) 4 where TUser : class 5 where TRole : class 6 { 7 // Services used by identity 8 services.AddOptions(); 9 services.AddAuthentication(options => 10 { 11 // This is the Default value for ExternalCookieAuthenticationScheme 12 options.SignInScheme = new IdentityCookieOptions().ExternalCookieAuthenticationScheme; 13 }); 14 15 // Identity services 16 services.TryAddSingleton<IdentityMarkerService>(); 17 services.TryAddScoped<IUserValidator<TUser>, UserValidator<TUser>>(); 18 services.TryAddScoped<IPasswordValidator<TUser>, PasswordValidator<TUser>>(); 19 services.TryAddScoped<IPasswordHasher<TUser>, PasswordHasher<TUser>>(); 20 services.TryAddScoped<ILookupNormalizer, UpperInvariantLookupNormalizer>(); 21 services.TryAddScoped<IRoleValidator<TRole>, RoleValidator<TRole>>(); 22 // No interface for the error describer so we can add errors without rev'ing the interface 23 services.TryAddScoped<IdentityErrorDescriber>(); 24 services.TryAddScoped<ISecurityStampValidator, SecurityStampValidator<TUser>>(); 25 services.TryAddScoped<IUserClaimsPrincipalFactory<TUser>, UserClaimsPrincipalFactory<TUser, TRole>>(); 26 services.TryAddScoped<UserManager<TUser>, UserManager<TUser>>(); 27 services.TryAddScoped<SignInManager<TUser>, SignInManager<TUser>>(); 28 services.TryAddScoped<RoleManager<TRole>, RoleManager<TRole>>(); 29 30 if (setupAction != null) 31 { 32 services.Configure(setupAction); 33 } 34 35 return new IdentityBuilder(typeof(TUser), typeof(TRole), services); 36 }
原来这货把认证授权需要实现的接口都注册了一遍,光看方法名字确实让人很难想象。
那么后两行是干什么的呢?看一下AddIdentity源代码的26~28行,需要用到usermanger、signinmanager和rolemanager,这几个类里面需要用到IUserStore和IRoleStore,这两行就是注册这两个接口的实现的。
好了,上面是注册的,那么光注册还不行,还要启用才行,咱们再看一下Configure方法,添加如下代码:
1 public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerfactory) 2 { 3 ... 4 app.UseIdentity(); 5 ... 6 7 }
这样就告诉系统要启用认证这套机制了。
OK。到这里基础工作都做完了。
是不是活都干完了?没有啊,还没有告诉我怎么登录,怎么对网页或方法应用角色权限啊,我们慢慢来。
我们再写一个简单的登录和退出的管理类,假设叫AccountController吧,代码如下:
1 [Route("account")] 2 public class AccountController : Controller 3 { 4 [FromServices] 5 public SignInManager<HDUser> SignInManager { get; set; } 6 7 8 [AllowAnonymous] 9 [HttpGet("login")] 10 public IActionResult Login(string returnUrl) 11 { 12 ViewBag.ReturnUrl = returnUrl; 13 return View(); 14 } 15 16 [HttpPost("login")] 17 [AllowAnonymous] 18 public async Task<IActionResult> Login(LoginViewModel model, string returnUrl) 19 { 20 if (ModelState.IsValid == true) 21 { 22 var result = await SignInManager.PasswordSignInAsync(model.UserName, model.Password, model.RememberMe, lockoutOnFailure: false); 23 if (result.Succeeded) 24 { 25 26 return RedirectToLocal(returnUrl); 27 } 28 29 ModelState.AddModelError("", "用户名或密码错误."); 30 return View(model); 31 } 32 return View(model); 33 } 34 35 [HttpPost] 36 [ValidateAntiForgeryToken] 37 public async Task<IActionResult> LogOff() 38 { 39 await SignInManager.SignOutAsync(); 40 return RedirectToAction(nameof(HomeController.Index), "Home"); 41 } 42 43 private IActionResult RedirectToLocal(string returnUrl) 44 { 45 if (Url.IsLocalUrl(returnUrl)) 46 { 47 return Redirect(returnUrl); 48 } 49 else 50 { 51 return RedirectToAction(nameof(HomeController.Index), "Home"); 52 } 53 } 54 55 }
这里面最重要的就是Login方法,它根据传入的登录信息,调用SignInManager.PasswordSignInAsync方法,就能完成登录了。等等,这个SignInManager怎么来的?你咋可以直接用呢?看看上面的定义:
1 [FromServices] 2 public SignInManager<HDUser> SignInManager { get; set; }
有一个FromServices标记,原来这个是系统自动从服务里面取得的,还记得我们上面调用了一次AddIdentity吗?它就把SignInManager注册到服务里,然后就可以到处使用了,这个可以认为是asp.net5的非常重要的依赖注入机制,也是灵魂之一,如果不明白的,一定要好好看看哦。
好了,我们再看看LoginViewModel,这个也是比较重要的一个参数:
1 public class LoginViewModel 2 { 3 [Required] 4 [Display(Name = "User name")] 5 public string UserName { get; set; } 6 7 [Required] 8 [DataType(DataType.Password)] 9 [Display(Name = "Password")] 10 public string Password { get; set; } 11 12 [Display(Name = "Remember me?")] 13 public bool RememberMe { get; set; } 14 }
不过代码很简单,vs自动生成项目的时候,一般都这么生成登录代码。
有Controler了,是不是还要有View呢,当然要有,不过很简单,就不贴出来了。
最后,我们看一下应用权限的类是什么样的:
[Authorize] [Route("api/data")] public class DatasApiController : Controller { [HttpGet("daxialist")] [Authorize(Roles ="user")] public JsonResult GetDaxiaList() { ... return Json(...); } [HttpGet("daxiacount")] public JsonResult GetDaxiaCount() { return Json(...); } }
这里使用[Authorize]表示要对这个类的所有方法应用权限管理
[Authorize(Roles ="user")]表示这个方法只能角色为user的用户可以访问。
那个GetDaxiaCount方法,没有应用角色,表示所有经过认证的用户都可以访问,不管角色是什么。
至此,我们最终完成了一个最简单的基于asp.net5的认证授权。当然,这里可能连认证授权百分之一的功能都没有体现出来,要想深入了解,还要花大量的时间去研究啊。