Asp.Net Core 5 之 JWT验证

通用JWT工作流
用户发送凭证给网站登录.
网站后端验证凭证, 声明合适的声明然后生成JWT并返回给用户.
用户获取JWT直到过期, 在后继的请求中奖JWT发送给网站.
网站验证JWT后决定资源是是否可访问.
配置
.NET Core提供了NuGet 包可以直接安装, 省去很多工作. Microsoft.AspNetCore.Authentication.JwtBearer
两种使用方法:
app.UseJwtBearerAuthentication()
services.AddJwtBearer()
在Startup.cs中进行注册服务和使用.
public void ConfigureServices(IServiceCollection services)
{
var jwtTokenConfig = Configuration.GetSection("jwtTokenConfig").Get();
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过期时间验证的允许时间
};
});
// ...
}
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
    };
}

http://www.zgswcn.com/article/202012/202012281253301142.html
http://www.zgswcn.com/article/202012/202012251624131222.html
http://www.zgswcn.com/article/202012/202012251539471203.html
http://www.zgswcn.com/article/202012/202012192152571037.html
private static string GenerateRefreshTokenString()
{
var randomNumber = new byte[32];
using var randomNumberGenerator = RandomNumberGenerator.Create();
randomNumberGenerator.GetBytes(randomNumber);
return Convert.ToBase64String(randomNumber);
}
}
如果想要通过HttpContext.User.FindFirstValue(""), 需要在容器中注册services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
通过写一个静态的扩展方法进行获取服务:
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();
Utilities.HttpContext.Configure(httpContextAccessor);
return app;
}

验证通过后通过这种方式获取JWT的信息:
Context.User.FindFirstValue(JwtRegisteredClaimNames.Jti)
?? Context.User.FindFirstValue(ClaimTypes.NameIdentifier);
需要注意:

  1. 在控制器上需要添加[Authorize]的Attribute, 否则不生效.
  2. 前端请求服务是header需要添加Bearer: header["Authorization"]="Bearer "+token, 注意: Bearer后面有一个空格.
    在Startup.cs中需要使用:
    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
    ...
    app.UseAuthentication();
    app.UseAuthorization();
    ...
    }

不建议通过token传递敏感信息, key加密.
为了开发, 减少冗余, 可将JWT的属性进行封装, 将一些参数放在appsetting.json中, 通过Option方式动态获取, 使得项目更为灵活.

Asp.Net Core 5 之 JWT验证

上一篇:【Linux相识相知】yum的配置使用和程序包的编译安装


下一篇:Ubuntu 切换PHP版本