在身份認證中,若是某個Action須要權限才能訪問,最開始的想法就是,哪一個Action須要權限才能訪問,咱們寫個特性標註到上面便可,[TypeFilter(typeof(CustomAuthorizeActionFilterAttribute))]數據庫
/// <summary> /// 這是一個Action的Filter` 可是用做權限驗證 /// </summary> public class CustomAuthorizeActionFilterAttribute : Attribute, IActionFilter { private ILogger<CustomAuthorizeActionFilterAttribute> _logger = null; public CustomAuthorizeActionFilterAttribute(ILogger<CustomAuthorizeActionFilterAttribute> logger) { this._logger = logger; } public void OnActionExecuting(ActionExecutingContext context) { //取出Session var strUser = context.HttpContext.Session.GetString("CurrentUser"); if (!string.IsNullOrWhiteSpace(strUser)) { CurrentUser currentUser = Newtonsoft.Json.JsonConvert.DeserializeObject<CurrentUser>(strUser); _logger.LogDebug($"userName is {currentUser.Name}"); } else { context.Result = new RedirectResult("~/Fourth/Login"); } } public void OnActionExecuted(ActionExecutedContext context) { //context.HttpContext.Response.WriteAsync("ActionFilter Executed!"); Console.WriteLine("ActionFilter Executed!"); //this._logger.LogDebug("ActionFilter Executed!"); } }
固然了,要先在服務裏面使用Session的服務==》services.AddSession();cookie
可是這樣很差。.Net Core框架下,有一個特性Authorize,當咱們須要使用的時候,在某個Action上面標註便可app
[Authorize] public IActionResult Center() { return Content("Center"); }
咱們來運行看一下,會報異常框架
由於咱們沒有使用服務,在.Net Core下面,是默認不啓用受權過濾器的。這也是.Net Core框架的一個好處,咱們須要的時候才進行使用。框架作的少,更輕。async
下面咱們在服務裏面使用受權過濾器的服務ide
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme). AddCookie(CookieAuthenticationDefaults.AuthenticationScheme, o => { o.LoginPath = new PathString("/Home/Login"); });
再次瀏覽剛纔的頁面,這樣就會請求到登陸頁面,會把剛纔請求的頁面當作一個參數ui
固然也要使用app.UseAuthentication();這個中間件。this
在.Net Core裏面,保存登陸狀態,也是經過Cookie的方式。使用ClaimsIdentity與ClaimsPrincipalspa
public ActionResult Login(string name, string password) { this._ilogger.LogDebug($"{name} {password} 登錄系統"); #region 這裏應該是要到數據庫中查詢驗證的 CurrentUser currentUser = new CurrentUser() { Id = 123, Name = "Bingle", Account = "Administrator", Password = "123456", Email = "415473422@qq.com", LoginTime = DateTime.Now, Role = name.Equals("Bingle") ? "Admin" : "User" }; #endregion #region cookie { ////就很像一個CurrentUser,轉成一個claimIdentity var claimIdentity = new ClaimsIdentity("Cookie"); claimIdentity.AddClaim(new Claim(ClaimTypes.NameIdentifier, currentUser.Id.ToString())); claimIdentity.AddClaim(new Claim(ClaimTypes.Name, currentUser.Name)); claimIdentity.AddClaim(new Claim(ClaimTypes.Email, currentUser.Email)); claimIdentity.AddClaim(new Claim(ClaimTypes.Role, currentUser.Role)); claimIdentity.AddClaim(new Claim(ClaimTypes.Sid, currentUser.Id.ToString())); var claimsPrincipal = new ClaimsPrincipal(claimIdentity); base.HttpContext.SignInAsync(claimsPrincipal).Wait();//不就是寫到cookie } #endregion return View(); }
再次進行登陸,咱們就能夠看到這樣一個Cookiecode
在這以後,咱們再去訪問Genter頁面,發現仍是和以前返回的結果同樣,仍是訪問不到。這是爲何呢?是由於咱們在Action上面打的標籤[Authorize],什麼都沒給,咱們作下修改
[Authorize(AuthenticationSchemes = CookieAuthenticationDefaults.AuthenticationScheme)] public IActionResult Center() { return Content("Center"); }
如今咱們再次進行訪問,發現就能夠訪問成功了
經過User.FindFirstValue(ClaimTypes.Sid);這種方式,能夠獲取到咱們存入的值。
Scheme、Policy擴展
Scheme
#region 設置本身的schema的handler services.AddAuthenticationCore(options => options.AddScheme<MyHandler>("myScheme", "demo myScheme")); #endregion #region Schame 驗證 services.AddAuthentication(options => { options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;// "Richard";// }) .AddCookie(options => { options.LoginPath = new PathString("/Fourth/Login");// 這裏指定若是驗證不經過就跳轉到這個頁面中去 options.ClaimsIssuer = "Cookie"; });
MyHandler類:
/// <summary> /// 自定義的handler /// 一般會提供一個統一的認證中心,負責證書的頒發及銷燬(登入和登出),而其它服務只用來驗證證書,並用不到SingIn/SingOut。 /// </summary> public class MyHandler : IAuthenticationHandler, IAuthenticationSignInHandler, IAuthenticationSignOutHandler { public AuthenticationScheme Scheme { get; private set; } protected HttpContext Context { get; private set; } public Task InitializeAsync(AuthenticationScheme scheme, HttpContext context) { Scheme = scheme; Context = context; return Task.CompletedTask; } /// <summary> /// 認證 /// </summary> /// <returns></returns> public async Task<AuthenticateResult> AuthenticateAsync() { var cookie = Context.Request.Cookies["myCookie"]; if (string.IsNullOrEmpty(cookie)) { return AuthenticateResult.NoResult(); } return AuthenticateResult.Success(this.Deserialize(cookie)); } /// <summary> /// 沒有登陸 要求 登陸 /// </summary> /// <param name="properties"></param> /// <returns></returns> public Task ChallengeAsync(AuthenticationProperties properties) { Context.Response.Redirect("/login"); return Task.CompletedTask; } /// <summary> /// 沒權限 /// </summary> /// <param name="properties"></param> /// <returns></returns> public Task ForbidAsync(AuthenticationProperties properties) { Context.Response.StatusCode = 403; return Task.CompletedTask; } /// <summary> /// 登陸 /// </summary> /// <param name="user"></param> /// <param name="properties"></param> /// <returns></returns> public Task SignInAsync(ClaimsPrincipal user, AuthenticationProperties properties) { var ticket = new AuthenticationTicket(user, properties, Scheme.Name); Context.Response.Cookies.Append("myCookie", this.Serialize(ticket)); return Task.CompletedTask; } /// <summary> /// 退出 /// </summary> /// <param name="properties"></param> /// <returns></returns> public Task SignOutAsync(AuthenticationProperties properties) { Context.Response.Cookies.Delete("myCookie"); return Task.CompletedTask; } private AuthenticationTicket Deserialize(string content) { byte[] byteTicket = System.Text.Encoding.Default.GetBytes(content); return TicketSerializer.Default.Deserialize(byteTicket); } private string Serialize(AuthenticationTicket ticket) { //須要引入 Microsoft.AspNetCore.Authentication byte[] byteTicket = TicketSerializer.Default.Serialize(ticket); return Encoding.Default.GetString(byteTicket); } }
Policy
#region 支持 policy 認證受權的服務 // 指定經過策略驗證的策略列 services.AddSingleton<IAuthorizationHandler, AdvancedRequirement>(); services.AddAuthorization(options => { //AdvancedRequirement能夠理解爲一個別名 options.AddPolicy("AdvancedRequirement", policy => { policy.AddRequirements(new NameAuthorizationRequirement("1")); }); }).AddAuthentication(options => { options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme; }) .AddCookie(options => { options.LoginPath = new PathString("/Fourth/Login"); options.ClaimsIssuer = "Cookie"; }); #endregion
AdvancedRequirement類:
/// <summary> /// Policy 的策略 或者是規則 /// </summary> public class AdvancedRequirement : AuthorizationHandler<NameAuthorizationRequirement>, IAuthorizationRequirement { protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, NameAuthorizationRequirement requirement) { // 這裏能夠把用戶信息獲取到之後經過數據庫進行驗證 // 這裏就能夠作一個規則驗證 // 也能夠經過配置文件來驗證 if (context.User != null && context.User.HasClaim(c => c.Type == ClaimTypes.Sid)) { string sid = context.User.FindFirst(c => c.Type == ClaimTypes.Sid).Value; if (!sid.Equals(requirement.RequiredName)) { context.Succeed(requirement); } } return Task.CompletedTask; } }
還須要在Configure方法中對中間件進行使用
app.UseSession(); app.UseCookiePolicy(); // app.UseAuthentication(); // 標識在當前系統中使用這個權限認證
總結:
在.Net Framwork環境受權通常來講是這個樣子的,在登陸的時候寫入Session,在須要控制權限的方法上標機一個權限特性,實如今方法執行前對Session進行判斷,若是有Session,就有權限。可是這種方式比較侷限。
.Net Core下的權限認證,來自於AuthenticationHttpContextExtensions擴展。
6大方法,能夠自行擴展這6個方法:須要自定義一個handler,handler須要繼承實現IAuthenticationHandler,IAuthenticationSignInHandler,IAuthenticationSignOutHandler。分別實現6個方法,須要制定在Core中使用。services.AddAuthenticationCore(options => options.AddScheme<MyHandler>("myScheme", "demo myScheme"));
若是使用了Sechme驗證,驗證不經過的時候,就默認跳轉到Account/Login?ReturnUrl=......。權限驗證來自於IAuthentizeData:AuthenticationSchemes Policy Roles。權限驗證支持Action、控制器、全局三種註冊方式。