欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

.net core webapi集成jwt实现身份认证

程序员文章站 2024-01-29 17:43:52
...

为什么使用JWT?
JWT实际上就是一个字符串,它由三部分组成,头部、载荷与签名。JWT不仅可用于认证,还可用于信息交换。善用JWT有助于减少服务器请求数据库的次数。适用于多客户端的前后端解决方案,JWT 是无状态化的,更适用于 RESTful 风格的接口验证。本文主要介绍使用JWT进行接口身份认证。

一.准备工作
(1).添加NuGet程序包
Microsoft.AspNetCore.Authentication.JwtBearer

(2).appsettings.json

  "JwtConfig": {
    "SecretKey": "d0ecd23c-dfdb-4005-a2ea-0fea210c858a", // **
    "Issuer": "liucheng", // 颁发者
    "Audience": "liucheng", // 接收者
    "Expired": 30 // 过期时间(30min)
  },

二.创建JWT配置类

 /// <summary>
    /// jwt配置
    /// </summary>
    public class JwtConfig : IOptions<JwtConfig>
    {
        public JwtConfig Value => this;
        public string SecretKey { get; set; }
        public string Issuer { get; set; }
        public string Audience { get; set; }
        public int Expired { get; set; }
        public DateTime NotBefore => DateTime.UtcNow;
        public DateTime IssuedAt => DateTime.UtcNow;
        public DateTime Expiration => IssuedAt.AddMinutes(Expired);
        private SecurityKey SigningKey => new SymmetricSecurityKey(Encoding.UTF8.GetBytes(SecretKey));
        public SigningCredentials SigningCredentials =>
            new SigningCredentials(SigningKey, SecurityAlgorithms.HmacSha256);
    }

三.创建JWT工具类

 public class GenerateJwt
    {
        private readonly JwtConfig _jwtConfig;
        public GenerateJwt(IOptions<JwtConfig> jwtConfig)
        {
            _jwtConfig = jwtConfig.Value;
        }
        /// <summary>
        /// 生成token
        /// </summary>
        /// <param name="sub"></param>
        /// <param name="customClaims">携带的用户信息</param>
        /// <returns></returns>
        public JwtTokenResult GenerateEncodedTokenAsync(string sub, LoginUserModel customClaims)
        {
            //创建用户身份标识,可按需要添加更多信息
            var claims = new List<Claim>
            {
                new Claim("userid", customClaims.userid),
                new Claim("username", customClaims.username),
                new Claim("realname",customClaims.realname),
                new Claim("roles", string.Join(";",customClaims.roles)),
                new Claim("permissions", string.Join(";",customClaims.permissions)),
                new Claim("normalPermissions", string.Join(";",customClaims.normalPermissions)),
                new Claim(JwtRegisteredClaimNames.Sub, sub),
            };
            //创建令牌
            var jwt = new JwtSecurityToken(
                issuer: _jwtConfig.Issuer,
                audience: _jwtConfig.Audience,
                claims: claims,
                notBefore: _jwtConfig.NotBefore,
                expires: _jwtConfig.Expiration,
                signingCredentials: _jwtConfig.SigningCredentials);
            string access_token = new JwtSecurityTokenHandler().WriteToken(jwt);
            return new JwtTokenResult()
            {
                access_token = access_token,
                expires_in = _jwtConfig.Expired * 60,
                token_type = JwtBearerDefaults.AuthenticationScheme,
                user = customClaims
            };
        }
    }

四.JwtTokenResult
登录成功生成jwt返回给前端的model,LoginUserModel是用户信息model,我这里携带是为了避免前端解析token。

/// <summary>
    /// 登录成功返回model
    /// </summary>
    public class JwtTokenResult
    {
        public string access_token { get; set; }
        public string refresh_token { get; set; }
        /// <summary>
        /// 过期时间(单位秒)
        /// </summary>
        public int expires_in { get; set; }
        public string token_type { get; set; }
        public LoginUserModel user { get; set; }
    }

    public class LoginUserModel
    {
        public string userid { get; set; }
        public string username { get; set; }
        public string realname { get; set; }
        public string roles { get; set; }
        public string permissions { get; set; }
        public string normalPermissions { get; set; }
    }

五.JwtUser,用户解析jwt 获取当前用户信息

public static class JwtUser
    {
        /// <summary>
        /// 解析jwt 获取当前用户信息
        /// </summary>
        /// <param name="request"></param>
        /// <returns>用户信息</returns>
        public static LoginUserModel GetRequestUser(this HttpRequest request)
        {
            var authorization = request.Headers["Authorization"].ToString();
            var auth = authorization.Split(" ")[1];
            var jwtArr = auth.Split('.');
            var dic = JsonConvert.DeserializeObject<Dictionary<string, string>>(Base64UrlEncoder.Decode(jwtArr[1]));
            //解析Claims
            var reqUser = new LoginUserModel
            {
                userid = dic["userid"],
                username = dic["username"],
                realname = dic["realname"],
                roles = dic["roles"],
                permissions = dic["permissions"],
                normalPermissions = dic["normalPermissions"],
            };
            return reqUser;
        }

六.Startup配置
(1) .ConfigureServices注入jwt

             //注入jwt
            services.AddScoped<GenerateJwt>();
            services.Configure<JwtConfig>(Configuration.GetSection("JwtConfig"));

            #region jwt验证
            var jwtConfig = new JwtConfig();
            Configuration.Bind("JwtConfig", jwtConfig);
            services
                .AddAuthentication(option =>
                {
                    //认证middleware配置
                    option.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
                    option.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
                })
                .AddJwtBearer(options =>
                {
                    options.TokenValidationParameters = new TokenValidationParameters
                    {
                        //Token颁发机构
                        ValidIssuer = jwtConfig.Issuer,
                        //颁发给谁
                        ValidAudience = jwtConfig.Audience,
                        //这里的key要进行加密
                        IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtConfig.SecretKey)),
                        //是否验证Token有效期,使用当前时间与Token的Claims中的NotBefore和Expires对比
                        ValidateLifetime = true,
                    };
                });
            #endregion

(2).Configure

 app.UseAuthentication();//jwt

七.登录接口生成jwt(部分代码)
这里需要注意,将refreshToken存入redis 设置15天过期

        var claims = new LoginUserModel()
                {
                    userid = user.Id.ToString(),
                    username = user.UserName,
                    realname = user.RealName,
                    roles = string.Join(";", rolesReturn),
                    permissions = string.Join(";", permissionReturn),
                    normalPermissions = string.Join(";", PermissionHelper.GetPermission(2)),
                };
                var refreshToken = Guid.NewGuid().ToString();
                //refreshToken设置15天过期
                await RedisHelper.SetAsync(refreshToken, claims, 60 * 60 * 24 * 15);
                var jwtTokenResult = _generateJwt.GenerateEncodedTokenAsync(user.Id.ToString(), claims);
                jwtTokenResult.refresh_token = refreshToken;

八.刷新token
设置的token有效期为30min,前端拿到token后需要定时刷新token(在token失效临界点刷新)。
根据refreshToken获取新的token,首先验证在redis中能否找到refreshToken,找不到则证明登录已过期,找到后则生成新的token。

         /// <summary>
        /// 刷新token
        /// </summary>
        /// <param name="refreshToken"></param>
        /// <returns></returns>
        [HttpGet]
        [Route("refreshToken")]
        public async Task<JsonResult> RefreshToken(string refreshToken)
        {
            if (string.IsNullOrEmpty(refreshToken))
                return AjaxHelper.JsonResult(HttpStatusCode.BadRequest, "refreshToken不能为空");

            var claims = await RedisHelper.GetAsync<LoginUserModel>(refreshToken);
            if (claims == null)
                return AjaxHelper.JsonResult(HttpStatusCode.Unauthorized, "登录已过期");

            var jwtTokenResult = _generateJwt.GenerateEncodedTokenAsync(claims.userid, claims);
            jwtTokenResult.refresh_token = refreshToken;
            return AjaxHelper.JsonResult(HttpStatusCode.OK, "成功", jwtTokenResult);//200
        }

九.使用
在接口上标记身份验证

.net core webapi集成jwt实现身份认证