.Net Core權限認證基於Cookie的認證&授權.Scheme、Policy擴展


在身份認證中,如果某個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();

但是這樣不好。.Net Core框架下,有一個特性Authorize,當我們需要使用的時候,在某個Action上面標注即可

 [Authorize]
 public IActionResult Center()
 {
     return Content("Center");
 }

我們來運行看一下,會報異常

 

 因為我們沒有使用服務,在.Net Core下面,是默認不啟用授權過濾器的。這也是.Net Core框架的一個好處,我們需要的時候才進行使用。框架做的少,更輕。

下面我們在服務里面使用授權過濾器的服務

services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme).
    AddCookie(CookieAuthenticationDefaults.AuthenticationScheme,
o =>
    {
        o.LoginPath = new PathString("/Home/Login");
    });

再次瀏覽剛才的頁面,這樣就會請求到登錄頁面,會把剛才請求的頁面當做一個參數

 

當然也要使用app.UseAuthentication();這個中間件。

在.Net Core里面,保存登錄狀態,也是通過Cookie的方式。使用ClaimsIdentity與ClaimsPrincipal

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();
}

再次進行登錄,我們就可以看到這樣一個Cookie

 

 在這之后,我們再去訪問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、控制器、全局三種注冊方式。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM