zoukankan      html  css  js  c++  java
  • AspNetCore3.0 和 JWT

    添加NuGet引用

    IdentityModel
    Microsoft.AspNetCore.Authorization.JwtBearer
    

    在appsettings.json中添加JwtBearer配置

    "Authentication": {
        "JwtBearer": {
          "IsEnabled": "true",
          "SecurityKey": "JWTStudyWebsite_DI20DXU3",
          "Issuer": "JWTStudy",
          "Audience": "JWTStudyWebsite"
        }
    }
    

    创建JWT服务注册扩展

    public static class JwtConfiguration
    {
        public static void AddJwtConfiguration(this IServiceCollection services, IConfiguration configuration)
        {
            if (bool.Parse(configuration["Authentication:JwtBearer:IsEnabled"]))
            {
                services.AddAuthentication(options => {
                    options.DefaultAuthenticateScheme = "JwtBearer";
                    options.DefaultChallengeScheme = "JwtBearer";
                }).AddJwtBearer("JwtBearer", options =>
                {
                    options.Audience = configuration["Authentication:JwtBearer:Audience"];
    
                    options.TokenValidationParameters = new TokenValidationParameters
                    {
                        // The signing key must match!
                        ValidateIssuerSigningKey = true,
                        IssuerSigningKey = new SymmetricSecurityKey(
                            Encoding.ASCII.GetBytes(configuration["Authentication:JwtBearer:SecurityKey"])),
    
                        // Validate the JWT Issuer (iss) claim
                        ValidateIssuer = true,
                        ValidIssuer = configuration["Authentication:JwtBearer:Issuer"],
    
                        // Validate the JWT Audience (aud) claim
                        ValidateAudience = true,
                        ValidAudience = configuration["Authentication:JwtBearer:Audience"],
    
                        // Validate the token expiry
                        ValidateLifetime = true,
    
                        // If you want to allow a certain amount of clock drift, set that here
                        ClockSkew = TimeSpan.Zero
                    };
                });
            }
        }
    }
    

    在startup>ConfigureServices中注册服务

    services.AddJwtConfiguration(Configuration);
    

    创建AccessTokenController

    说明:用户首次使用用户名和密码登录,生成AccessToken和RefreshToken,
    其中AccessToken的有效时间为30分钟,RefreshToken的有效时间为60分钟。
    

    可能的情况

    • AccessToken没有过期
    • AccessToken已过期,RefreshToken未过期
    • RefreshToken已过期

    一、首先创建一个方法,用于生成AccessToken

    private string GetAccessToken(SessionUser user)
    {
        var claims = new[]
        {
            new Claim(JwtClaimTypes.Id, user.Id.ToString()),
            new Claim(JwtClaimTypes.Name, user.Name),
            new Claim(JwtClaimTypes.Role, "user")
        };
    
        var key = new SymmetricSecurityKey(
            Encoding.UTF8.GetBytes(_configuration["Authentication:JwtBearer:SecurityKey"]));
        var credentials = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
    
        var token = new JwtSecurityToken(
            _configuration["Authentication:JwtBearer:Issuer"],
            _configuration["Authentication:JwtBearer:Audience"],
            claims,
            expires: DateTime.Now.AddMinutes(30),
            signingCredentials: credentials
        );
    
        return new JwtSecurityTokenHandler().WriteToken(token);
    }
    

    二、通过用户名密码获取AccessToken

    [HttpPost]
    public IActionResult Post([FromBody]LoginModel model)
    {
        if (!string.IsNullOrWhiteSpace(model.Account) && !string.IsNullOrWhiteSpace(model.Pw))
        {
            var user = new SessionUser
            {
                Id = 1,
                Name = "admin",
                Role = "user"
            };
    
            var refreshToken = Guid.NewGuid().ToString("N");
            var refreshTokenExpiredTime = DateTime.Now.AddMinutes(60);
    
            var cacheKey = $"RefreshToken:{refreshToken}";
            var cacheValue = JsonConvert.SerializeObject(user);
    
            _cache.SetString(cacheKey, cacheValue,
                new DistributedCacheEntryOptions
                {
                    AbsoluteExpiration = refreshTokenExpiredTime
                });
    
            return Ok(new
            {
                AccessToken = GetAccessToken(user),
                Code = 200,
                RefreshTokenExpired = DateTimeHelper.ConvertToLong(refreshTokenExpiredTime),
                RefreshToken = refreshToken
            });
        }
    
        return Ok(new { Code = 0, Token = "" });
    }
    

    三、通过RefreshToken获取新的AccessToken

    [Authorize]
    [HttpPost("Refresh")]
    public IActionResult Refresh(RefreshTokenRequest request)
    {
        var token = request.Token;
        var cacheStr = _cache.GetString($"RefreshToken:{token}");
        if (string.IsNullOrWhiteSpace(cacheStr))
        {
            return Ok(new
            {
                Code = 0,
                Message = "Token不存在或已过期"
            });
        }
    
        var cacheUser = JsonConvert.DeserializeObject<SessionUser>(cacheStr);
        var userId = User.Claims.First(c => c.Type == JwtClaimTypes.Id);
    
        if (userId == null || cacheUser.Id.ToString() != userId.Value)
        {
            return Ok(new
            {
                Code = 0,
                Message = "用户不匹配"
            });
        }
    
        var refreshToken = Guid.NewGuid().ToString("N");
        var cacheKey = $"RefreshToken:{refreshToken}";
        var refreshTokenExpiredTime = DateTime.Now.AddMinutes(60);
    
        _cache.SetString(cacheKey, cacheStr, new DistributedCacheEntryOptions
        {
            AbsoluteExpiration = DateTime.Now.AddMinutes(30)
        });
    
        return Ok(new
        {
            AccessToken = GetAccessToken(cacheUser),
            Code = 200,
            RefreshTokenExpired = DateTimeHelper.ConvertToLong(refreshTokenExpiredTime),
            RefreshToken = refreshToken
        });
    }
    

    完整代码

    public class LoginModel
    {
        [Required]
        public string Account { get; set; }
    
        [Required]
        public string Pw { get; set; }
    }
    
    public class SessionUser
    {
        public int Id { get; set; }
    
        public string Name { get; set; }
    
        public string Role { get; set; }
    }
    
    public class DateTimeHelper
    {
        /// <summary>
        /// DateTime转时间戳
        /// </summary>
        /// <param name="date"></param>
        /// <returns></returns>
        public static long ConvertToLong(DateTime date)
        {
            var startTime = TimeZoneInfo.ConvertTimeFromUtc(new DateTime(1970, 1, 1), TimeZoneInfo.Utc);
            return (new DateTimeOffset(date).UtcTicks - startTime.Ticks) / 10000;
        }
    
        /// <summary>
        /// 时间戳转DateTime
        /// </summary>
        /// <param name="timestamp"></param>
        /// <returns></returns>
        public static DateTime ConvertToDateTime(long timestamp)
        {
            var startTime = TimeZoneInfo.ConvertTimeFromUtc(new DateTime(1970, 1, 1), TimeZoneInfo.Local);
            return startTime.Add(new TimeSpan(timestamp * 10000));
        }
    }
    
    [Route("api/[controller]")]
    [ApiController]
    public class AccessTokenController : ControllerBase
    {
        private readonly IConfiguration _configuration;
        private readonly IDistributedCache _cache;
        private readonly UserService _service;
    
        public AccessTokenController(IConfiguration configuration, IDistributedCache cache, UserService service)
        {
            _configuration = configuration;
            _cache = cache;
            _service = service;
        }
    
        /// <summary>
        /// 登录,获取后原来RefreshToken将失效。
        /// AccessToken有效时间30分钟
        /// RefreshToken有效时间60分钟
        /// </summary>
        /// <param name="model"></param>
        /// <returns></returns>
        [HttpPost]
        public ActionResult Post([FromBody]LoginModel model)
        {
            var result = _service.Login(model.Account, model.Pw);
            if (result.Code != 200)
            {
                return Ok(new {Code = 0, Message = result.Message});
            }
    
            var user = new SessionUser
            {
                Id = result.Body.Id,
                Name = result.Body.NickName,
                Role = "user"
            };
    
            var refreshToken = Guid.NewGuid().ToString("N");
            var refreshTokenExpiredTime = DateTime.Today.AddDays(7);
    
            var cacheKey = $"RefreshToken:{refreshToken}";
            var cacheValue = JsonConvert.SerializeObject(user);
    
            _cache.SetString(cacheKey, cacheValue,
                new DistributedCacheEntryOptions
                {
                    AbsoluteExpiration = refreshTokenExpiredTime
                });
    
            return Ok(new
            {
                AccessToken = GetAccessToken(user),
                Code = 200,
                RefreshTokenExpired = DateTimeHelper.ConvertToLong(refreshTokenExpiredTime),
                RefreshToken = refreshToken
            });
        }
    
        /// <summary>
        /// 刷新AccessToken
        /// </summary>
        /// <param name="request">刷新的请求 {"token": "refresh_token"}</param>
        /// <returns></returns>
        [Authorize]
        [HttpPost("Refresh")]
        public IActionResult Refresh(RefreshTokenRequest request)
        {
            var token = request.Token;
            var cacheStr = _cache.GetString($"RefreshToken:{token}");
            if (string.IsNullOrWhiteSpace(cacheStr))
            {
                return Ok(new
                {
                    Code = 0,
                    Message = "Token不存在或已过期"
                });
            }
    
            var cacheUser = JsonConvert.DeserializeObject<SessionUser>(cacheStr);
            var userId = User.Claims.First(c => c.Type == JwtClaimTypes.Id);
    
            if (userId == null || cacheUser.Id.ToString() != userId.Value)
            {
                return Ok(new
                {
                    Code = 0,
                    Message = "用户不匹配"
                });
            }
    
            var refreshToken = Guid.NewGuid().ToString("N");
            var cacheKey = $"RefreshToken:{refreshToken}";
            var refreshTokenExpiredTime = DateTime.Today.AddDays(7);
    
            _cache.SetString(cacheKey, cacheStr, new DistributedCacheEntryOptions
            {
                AbsoluteExpiration = refreshTokenExpiredTime
            });
    
            return Ok(new
            {
                AccessToken = GetAccessToken(cacheUser),
                Code = 200,
                RefreshTokenExpired = DateTimeHelper.ConvertToLong(refreshTokenExpiredTime),
                RefreshToken = refreshToken
            });
        }
    
        /// <summary>
        /// 通过SessionUser获取AccessToken
        /// </summary>
        /// <param name="user"></param>
        /// <returns></returns>
        private string GetAccessToken(SessionUser user)
        {
            var claims = new[]
            {
                new Claim(JwtClaimTypes.Id, user.Id.ToString()),
                new Claim(JwtClaimTypes.Name, user.Name),
                new Claim(JwtClaimTypes.Role, "user")
            };
    
            var key = new SymmetricSecurityKey(
                Encoding.UTF8.GetBytes(_configuration["Authentication:JwtBearer:SecurityKey"]));
            var credentials = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
    
            var token = new JwtSecurityToken(
                _configuration["Authentication:JwtBearer:Issuer"],
                _configuration["Authentication:JwtBearer:Audience"],
                claims,
                expires: DateTime.Now.AddHours(2),
                signingCredentials: credentials
            );
    
            return new JwtSecurityTokenHandler().WriteToken(token);
        }
    
        /// <summary>
        /// 刷新AccessToken的请求
        /// </summary>
        public class RefreshTokenRequest
        {
            /// <summary>
            /// RefreshToken,登录后获取
            /// </summary>
            public string Token { get; set; }
        }
    }
    
  • 相关阅读:
    原型链与继承
    js错误处理Try-catch和throw
    函数柯里化
    js函数节流
    事件委托
    innerHTML属性的内存和性能问题
    微信小程序左滑显示按钮demo
    this的作用
    前端工作面试经典问题(超级全)
    HTML5入门指南
  • 原文地址:https://www.cnblogs.com/diwu0510/p/11562545.html
Copyright © 2011-2022 走看看