zoukankan      html  css  js  c++  java
  • 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方式动态获取, 使得项目更为灵活.
  • 相关阅读:
    C#中的委托,匿名方法和Lambda表达式
    模式化窗口问题![window.dialogArguments]
    js动态改变HiddenField值,后台不能获取值的问题
    将DataTable进行分页并生成新的DataTable
    出错提示为:该行已经属于另一个表 的解决方法
    如何在服务器端获得showModalDialog传递的参数
    Synchronized和SyncRoot与 集合类 的同步
    Oracle中数据出现####的问题
    如何防止多次提交按钮造成重复提交
    Invoke and BeginInvoke
  • 原文地址:https://www.cnblogs.com/baron-li/p/14299115.html
Copyright © 2011-2022 走看看