.Net Core WebApi集成JWT實現身份認證

爲什麼使用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
        }

九.使用
在接口上標記身份驗證

在這裏插入圖片描述

發佈了7 篇原創文章 · 獲贊 1 · 訪問量 422
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章