.Net Core WebApi集成JWT实现身份认证

2 篇文章 0 订阅
2 篇文章 0 订阅

为什么使用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
        }

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

在这里插入图片描述

  • 0
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值