asp.net core 3.1多種身份驗證方案,cookie和jwt混合認證授權

XSpringSun發表於2020-08-04

開發了一個公司內部系統,使用asp.net core 3.1。在開發使用者認證授權使用的是簡單的cookie認證方式,然後開發好了要寫幾個介面給其它系統呼叫資料。並且只是幾個簡單的介面不準備再重新部署一個站點,所以就直接在MVC的專案裡面加了一個API區域用來寫介面。這時候因為是介面所以就不能用cookie方式進行認證,得加一個jwt認證,採用多種身份驗證方案來進行認證授權。

認證授權

身份驗證是確定使用者身份的過程。 授權是確定使用者是否有權訪問資源的過程。 在 ASP.NET Core 中,身份驗證由 IAuthenticationService 負責,而它供身份驗證中介軟體使用。 身份驗證服務會使用已註冊的身份驗證處理程式來完成與身份驗證相關的操作。

認證-->授權

關於認證授權我們要區分認證和授權是兩個概念,具體可檢視MSDN官方文件也可以搜尋其它文章看看,講的很多。其中包括OAuth 2.0 以及jwt的相關知識都有很多資料並且講解的很好。

身份認證

身份驗證方案由 Startup.ConfigureServices 中的註冊身份驗證服務指定:
方式是在呼叫 services.AddAuthentication 後呼叫方案特定的擴充套件方法(例如 AddJwtBearer 或 AddCookie)。 這些擴充套件方法使用 AuthenticationBuilder.AddScheme 向適當的設定註冊方案。

  • 新增cookie JwtBearer驗證方案
public void ConfigureServices(IServiceCollection services)
{
    services.AddSession();
    services.AddMvc(o =>
    {
        o.Filters.Add(typeof(MyExceptionFilterAttribute));// 全域性異常Filter  
    }).AddRazorRuntimeCompilation();
    //新增身份認證方案
    var jwtConfig= Configuration.GetSection("Jwt").Get<JwtConfig>();
    services.AddAuthentication
        (authoption =>{
            //指定預設選項
            authoption.DefaultChallengeScheme= CookieAuthenticationDefaults.AuthenticationScheme;
            authoption.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;
            authoption.DefaultSignOutScheme = CookieAuthenticationDefaults.AuthenticationScheme;
            authoption.DefaultSignInScheme= CookieAuthenticationDefaults.AuthenticationScheme;
        })
   .AddCookie(CookieAuthenticationDefaults.AuthenticationScheme, option =>
   {
       option.Cookie.Name = "adCookie";//設定儲存使用者登入資訊(使用者Token資訊)的Cookie名稱
       option.Cookie.HttpOnly = true;//設定儲存使用者登入資訊(使用者Token資訊)的Cookie,無法通過客戶端瀏覽器指令碼(如JavaScript等)訪問到
       option.ExpireTimeSpan = TimeSpan.FromDays(3);// 過期時間
       option.SlidingExpiration = true;// 是否在過期時間過半的時候,自動延期
       option.LoginPath = "/Account/Login";
       option.LogoutPath = "/Account/LoginOut";
   })
   .AddJwtBearer(option =>
   {
       option.TokenValidationParameters = new TokenValidationParameters
       {
           ValidIssuer = jwtConfig.Issuer,
           ValidAudience = jwtConfig.Audience,
           ValidateIssuer = true,
           ValidateLifetime = jwtConfig.ValidateLifetime,
           IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtConfig.SigningKey)),
           //緩衝過期時間,總的有效時間等於這個時間加上jwt的過期時間
           ClockSkew = TimeSpan.FromSeconds(0)
       };
   });
}
  • JwtBearer認證的配置引數類JwtConfig
public class JwtConfig
{
    /// <summary>
    /// 誰頒發的
    /// </summary>
    public string Issuer { get; set; }

    /// <summary>
    /// 頒發給誰
    /// </summary>
    public string Audience { get; set; }

    /// <summary>
    /// 令牌密碼
    /// a secret that needs to be at least 16 characters long
    /// </summary>
    public string SigningKey { get; set; }

    /// <summary>
    /// 過期時間(分鐘)
    /// </summary>
    public int Expires { get; set; }

    /// <summary>
    /// 是否校驗過期時間
    /// </summary>
    public bool ValidateLifetime { get; set; }
}
  • appsettings.json 配置引數
  "Jwt": {
    "Issuer": "issuer",
    "Audience": "audience",
    "SigningKey": "c0d32c63-z43d-4917-bbc2-5e726d087452",
    //過期時間(分鐘)
    "Expires": 10080,
    //是否驗證過期時間
    "ValidateLifetime": true
  }
  • 新增身份驗證中介軟體

通過在應用的 IApplicationBuilder 上呼叫 UseAuthentication 擴充套件方法,在 Startup.Configure 中新增身份驗證中介軟體。 如果呼叫 UseAuthentication,會註冊使用之前註冊的身份驗證方案的中間節。 請在依賴於要進行身份驗證的使用者的所有中介軟體之前呼叫 UseAuthentication。 如果使用終結點路由,則必須按以下順序呼叫 UseAuthentication:
● 在 UseRouting之後呼叫,以便路由資訊可用於身份驗證決策。
● 在 UseEndpoints 之前呼叫,以便使用者在經過身份驗證後才能訪問終結點。

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }
    else
    {
        app.UseExceptionHandler("/Home/Error");
        app.UseHsts();
    }
    app.UseHttpsRedirection();
    app.UseSession();
    app.UseRouting();

    //開啟認證中介軟體
    app.UseAuthentication();
    //開啟授權中介軟體
    app.UseAuthorization();

    app.UseEndpoints(endpoints =>
    {

        endpoints.MapControllerRoute(
        name: "areas",
        pattern: "{area:exists}/{controller=Home}/{action=Index}/{id?}");

        endpoints.MapControllerRoute(
            name: "default",
            pattern: "{controller=Home}/{action=Index}/{id?}");
    });
}
  • cookie認證
[HttpPost]
public async Task<NewtonsoftJsonResult> LoginIn(string userName, string userPassword, string code)
{
    AjaxResult objAjaxResult = new AjaxResult();
    var user = _userBll.GetUser(userName, userPassword);
    if (user == null)
    {
        objAjaxResult.Result = DoResult.NoAuthorization;
        objAjaxResult.PromptMsg = "使用者名稱或密碼錯誤";
    }
    else
    {
        var claims = new List<Claim>
        {   
            new Claim("userName", userName),
            new Claim("userID",user.Id.ToString()),
        };
        await HttpContext.SignInAsync(new ClaimsPrincipal(new ClaimsIdentity(claims,CookieAuthenticationDefaults.AuthenticationScheme)));
        objAjaxResult.Result = DoResult.Success;
        objAjaxResult.PromptMsg = "登入成功";
    }
    return new NewtonsoftJsonResult(objAjaxResult);
}
  • jwt認證
[HttpPost]
public NewtonsoftJsonResult Token([FromBody] UserInfo model)
{
    AjaxResult objAjaxResult = new AjaxResult();
    var user = _userBll.GetUser(model.UserName, model.Password);
    if (user == null)
    {
        objAjaxResult.Result = DoResult.NoAuthorization;
        objAjaxResult.PromptMsg = "使用者名稱或密碼錯誤";
    }
    else
    {
        //jwtTokenOptions 是通過配置獲取上面配置的引數資訊
        var jwtTokenOptions = BaseConfigModel.jwtConfig;
        var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtTokenOptions.SigningKey));
        var credentials = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
        //身份
        var claims = new List<Claim>
            {
                new Claim("userID",user.Id.ToString()),
                new Claim("userName",user.UserName),
            };
        //令牌
        var expires = DateTime.Now.AddMinutes(jwtTokenOptions.Expires);
        var token = new JwtSecurityToken(
            issuer: jwtTokenOptions.Issuer,
            audience: jwtTokenOptions.Audience,
            claims: claims,
            notBefore: DateTime.Now,
            expires: expires,
            signingCredentials: credentials
            );
        string jwtToken = new JwtSecurityTokenHandler().WriteToken(token);
        objAjaxResult.Result = DoResult.Success;
        objAjaxResult.RetValue = new
        {
            token = jwtToken
        };
        objAjaxResult.PromptMsg = "登入成功";
    }
    return new NewtonsoftJsonResult(objAjaxResult);
}

授權

在授權時,應用指示要使用的處理程式。 選擇應用程式將通過以逗號分隔的身份驗證方案列表傳遞到來授權的處理程式 [Authorize] 。 [Authorize]屬性指定要使用的身份驗證方案或方案,不管是否配置了預設。

  • 預設授權
    因為上面認證配置中我們使用cookie作為預設配置,所以前端對應的controller就不用指定驗證方案,直接打上[Authorize]即可。
  • 選擇授權
    對於API介面我們使用Jwt授權,在Controller上打上指定方案。
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]

總結

關於多種方案混合驗證授權的流程:
1、配置認證方案(相關的配置引數可採用配置檔案形式)。
2、新增授權驗證中介軟體。
3、提供認證介面。
4、配置需要授權的介面授權方案。

相關文章