Asp.Net Core 5 之 JWT驗證


Asp.Net Core5 之 JWT

通用JWT工作流

  1. 用戶發送憑證給網站登錄.
  2. 網站后端驗證憑證, 聲明合適的聲明然后生成JWT並返回給用戶.
  3. 用戶獲取JWT直到過期, 在后繼的請求中獎JWT發送給網站.
  4. 網站驗證JWT后決定資源是是否可訪問.

配置

  1. .NET Core提供了NuGet 包可以直接安裝, 省去很多工作. Microsoft.AspNetCore.Authentication.JwtBearer
  2. 兩種使用方法:
  1. app.UseJwtBearerAuthentication()
  2. services.AddJwtBearer()
  1. 在Startup.cs中進行注冊服務和使用.
public void ConfigureServices(IServiceCollection services)
{
    var jwtTokenConfig = Configuration.GetSection("jwtTokenConfig").Get<JwtTokenConfig>();
    services.AddSingleton(jwtTokenConfig);
    services.AddAuthentication(x =>
    {
        x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
        x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
    }).AddJwtBearer(x =>
    {
        x.RequireHttpsMetadata = true;// default is true 為metadata或者authority驗證請求https
        x.SaveToken = true;// default is true, 將JWT保存到當前的HttpContext, 以至於可以獲取它通過await HttpContext.GetTokenAsync("Bearer","access_token"); 如果想設置為false, 將token保存在claim中, 然后獲取通過User.FindFirst("access_token")?.value.
        x.TokenValidationParameters = new TokenValidationParameters
        {// 設置參數用於驗證身份token
            ValidateIssuer = true,
            ValidIssuer = jwtTokenConfig.Issuer,
            ValidateIssuerSigningKey = true,
            IssuerSigningKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(jwtTokenConfig.Secret)),
            ValidAudience = jwtTokenConfig.Audience,
            ValidateAudience = true,
            ValidateLifetime = true,
            ClockSkew = TimeSpan.FromMinutes(1)//對token過期時間驗證的允許時間
        };
    });
    // ...
}
  1. JWTHelper
public class JwtAuthManager : IJwtAuthManager
{
    public IImmutableDictionary<string, RefreshToken> UsersRefreshTokensReadOnlyDictionary => _usersRefreshTokens.ToImmutableDictionary();
    private readonly ConcurrentDictionary<string, RefreshToken> _usersRefreshTokens;  // can store in a database or a distributed cache
    private readonly JwtTokenConfig _jwtTokenConfig;
    private readonly byte[] _secret;

    public JwtAuthManager(JwtTokenConfig jwtTokenConfig)
    {
        _jwtTokenConfig = jwtTokenConfig;
        _usersRefreshTokens = new ConcurrentDictionary<string, RefreshToken>();
        _secret = Encoding.ASCII.GetBytes(jwtTokenConfig.Secret);
    }

    public JwtAuthResult GenerateTokens(string username, Claim[] claims, DateTime now)
    {
        var shouldAddAudienceClaim = string.IsNullOrWhiteSpace(claims?.FirstOrDefault(x => x.Type == JwtRegisteredClaimNames.Aud)?.Value);
        var jwtToken = new JwtSecurityToken(
            _jwtTokenConfig.Issuer,
            shouldAddAudienceClaim ? _jwtTokenConfig.Audience : string.Empty,
            claims,
            expires: now.AddMinutes(_jwtTokenConfig.AccessTokenExpiration),
            signingCredentials: new SigningCredentials(new SymmetricSecurityKey(_secret), SecurityAlgorithms.HmacSha256Signature));
        var accessToken = new JwtSecurityTokenHandler().WriteToken(jwtToken);

        var refreshToken = new RefreshToken
        {
            UserName = username,
            TokenString = GenerateRefreshTokenString(),
            ExpireAt = now.AddMinutes(_jwtTokenConfig.RefreshTokenExpiration)
        };
        _usersRefreshTokens.AddOrUpdate(refreshToken.TokenString, refreshToken, (s, t) => refreshToken);

        return new JwtAuthResult
        {
            AccessToken = accessToken,
            RefreshToken = refreshToken
        };
    }

    private static string GenerateRefreshTokenString()
    {
        var randomNumber = new byte[32];
        using var randomNumberGenerator = RandomNumberGenerator.Create();
        randomNumberGenerator.GetBytes(randomNumber);
        return Convert.ToBase64String(randomNumber);
    }
}
  1. 如果想要通過HttpContext.User.FindFirstValue(""), 需要在容器中注冊services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
  1. 通過寫一個靜態的擴展方法進行獲取服務:
namespace SuperCosmos.Core.Utilities
{
    public static class HttpContext
    {
        private static IHttpContextAccessor _accessor;

 
        public static Microsoft.AspNetCore.Http.HttpContext Current => _accessor.HttpContext;


        internal static void Configure(IHttpContextAccessor accessor)
        {
            _accessor = accessor;
        }
    }
}


public static IApplicationBuilder UseStaticHttpContext(this IApplicationBuilder app)
        {
            var httpContextAccessor = app.ApplicationServices.GetRequiredService<IHttpContextAccessor>();
           Utilities.HttpContext.Configure(httpContextAccessor);
            return app;
        }

  1. 驗證通過后通過這種方式獲取JWT的信息:
Context.User.FindFirstValue(JwtRegisteredClaimNames.Jti)
                     ?? Context.User.FindFirstValue(ClaimTypes.NameIdentifier);
  1. 需要注意:
    1. 在控制器上需要添加[Authorize]的Attribute, 否則不生效.
    2. 前端請求服務是header需要添加Bearer: header["Authorization"]="Bearer "+token, 注意: Bearer后面有一個空格.
  1. 在Startup.cs中需要使用:
  public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
      ...
   app.UseAuthentication();
   app.UseAuthorization();
      ...
}


  1. 不建議通過token傳遞敏感信息, key加密.
  2. 為了開發, 減少冗余, 可將JWT的屬性進行封裝, 將一些參數放在appsetting.json中, 通過Option方式動態獲取, 使得項目更為靈活.


免責聲明!

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



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