.net core webapi jwt 更為清爽的認證 ,續期很簡單(1)


 

 

我的方式非主流,控制卻可以更加靈活,喜歡的朋友,不妨花一點時間學習一下

jwt認證分為兩部分,第一部分是加密解密,第二部分是靈活的應用於中間件,我的處理方式是將獲取token放到api的一個具體的controller中,將發放token與驗證分離,token的失效時間,發證者,使用者等信息存放到config中。

1.配置:

在appsettings.json中增加配置

"Jwt": {
"Issuer": "issuer",//隨意定義
"Audience": "Audience",//隨意定義
"SecretKey": "abc",//隨意定義
"Lifetime": 20, //單位分鍾
"ValidateLifetime": true,//驗證過期時間
"HeadField": "useless", //頭字段
"Prefix": "prefix", //前綴
"IgnoreUrls": [ "/Auth/GetToken" ]//忽略驗證的url
}

2:定義配置類:

internal class JwtConfig
    {
        public string Issuer { get; set; }
        public string Audience { get; set; }

        /// <summary>
        /// 加密key
        /// </summary>
        public string SecretKey { get; set; }
        /// <summary>
        /// 生命周期
        /// </summary>
        public int Lifetime { get; set; }
        /// <summary>
        /// 是否驗證生命周期
        /// </summary>
        public bool ValidateLifetime { get; set; }
        /// <summary>
        /// 驗證頭字段
        /// </summary>
        public string HeadField { get; set; }
        /// <summary>
        /// jwt驗證前綴
        /// </summary>
        public string Prefix { get; set; }
        /// <summary>
        /// 忽略驗證的url
        /// </summary>
        public List<string> IgnoreUrls { get; set; }
    }

3.加密解密接口:

 public interface IJwt
    {
        string GetToken(Dictionary<string, string> Clims);
        bool ValidateToken(string Token,out Dictionary<string ,string> Clims);
    }

4.加密解密的實現類:

 install-package System.IdentityModel.Tokens.Jwt 

 public class Jwt : IJwt
    {
        private IConfiguration _configuration;
        private string _base64Secret;
        private JwtConfig _jwtConfig = new JwtConfig();
        public Jwt(IConfiguration configration)
        {
            this._configuration = configration;
            configration.GetSection("Jwt").Bind(_jwtConfig);
            GetSecret();
        }
        /// <summary>
        /// 獲取到加密串
        /// </summary>
        private void GetSecret()
        {
            var encoding = new System.Text.ASCIIEncoding();
            byte[] keyByte = encoding.GetBytes("salt");
            byte[] messageBytes = encoding.GetBytes(this._jwtConfig.SecretKey);
            using (var hmacsha256 = new HMACSHA256(keyByte))
            {
                byte[] hashmessage = hmacsha256.ComputeHash(messageBytes);
                this._base64Secret= Convert.ToBase64String(hashmessage);
            }
        }
        /// <summary>
        /// 生成Token
        /// </summary>
        /// <param name="Claims"></param>
        /// <returns></returns>
        public string GetToken(Dictionary<string, string> Claims)
        {
            List<Claim> claimsAll = new List<Claim>();
            foreach (var item in Claims)
            {
                claimsAll.Add(new Claim(item.Key, item.Value??""));
            }
            var symmetricKey = Convert.FromBase64String(this._base64Secret);
            var tokenHandler = new JwtSecurityTokenHandler();
            var tokenDescriptor = new SecurityTokenDescriptor
            {
                Issuer = _jwtConfig.Issuer,
                Audience = _jwtConfig.Audience,
                Subject = new ClaimsIdentity(claimsAll),
                NotBefore = DateTime.Now,
                Expires = DateTime.Now.AddMinutes(this._jwtConfig.Lifetime),
                SigningCredentials =new SigningCredentials(new SymmetricSecurityKey(symmetricKey),
                                           SecurityAlgorithms.HmacSha256Signature)
            };
            var securityToken = tokenHandler.CreateToken(tokenDescriptor);
            return tokenHandler.WriteToken(securityToken);
        }
        public bool ValidateToken(string Token, out Dictionary<string, string> Clims)
        {
            Clims = new Dictionary<string, string>();
            ClaimsPrincipal principal = null;
            if (string.IsNullOrWhiteSpace(Token))
            {
                return false;
            }
            var handler = new JwtSecurityTokenHandler();
            try
            {
                var jwt = handler.ReadJwtToken(Token);

                if (jwt == null)
                {
                    return false;
                }
                var secretBytes = Convert.FromBase64String(this._base64Secret);
                var validationParameters = new TokenValidationParameters
                {
                    RequireExpirationTime = true,
                    IssuerSigningKey = new SymmetricSecurityKey(secretBytes),
                    ClockSkew = TimeSpan.Zero,
                    ValidateIssuer = true,//是否驗證Issuer
                    ValidateAudience = true,//是否驗證Audience
                    ValidateLifetime = this._jwtConfig.ValidateLifetime,//是否驗證失效時間
                    ValidateIssuerSigningKey = true,//是否驗證SecurityKey
                    ValidAudience = this._jwtConfig.Audience,
                    ValidIssuer = this._jwtConfig.Issuer
                };
                SecurityToken securityToken;
                principal = handler.ValidateToken(Token, validationParameters, out securityToken);
                foreach (var item in principal.Claims)
                {
                    Clims.Add(item.Type, item.Value);
                }
                return true;
            }
            catch (Exception ex)
            {
                return false;
            }
        }
    }

5.定義獲取Token的Controller:

在Startup.ConfigureServices中注入 IJwt

 services.AddTransient<IJwt, Jwt>();//Jwt注入 

[Route("[controller]/[action]")]
    [ApiController]
    public class AuthController : ControllerBase
    {
        private IJwt _jwt;
        public AuthController(IJwt jwt)
        {
            this._jwt = jwt;
        }
        /// <summary>
        /// getToken
        /// </summary>
        /// <returns></returns>
        [HttpPost]
        public IActionResult GetToken()
        {
            if (true)
            {
                Dictionary<string, string> clims = new Dictionary<string, string>();
                clims.Add("userName", userName);
                return new JsonResult(this._jwt.GetToken(clims));
            }
        }
    }

6.創建中間件:

 public class UseJwtMiddleware
    {
        private readonly RequestDelegate _next;
        private JwtConfig _jwtConfig =new JwtConfig();
        private IJwt _jwt;
        public UseJwtMiddleware(RequestDelegate next, IConfiguration configration,IJwt jwt)
        {
            _next = next;
            this._jwt = jwt;
            configration.GetSection("Jwt").Bind(_jwtConfig);
        }
        public Task InvokeAsync(HttpContext context)
        {
            if (_jwtConfig.IgnoreUrls.Contains(context.Request.Path))
            {
                return this._next(context);
            }
            else
            {
                if (context.Request.Headers.TryGetValue(this._jwtConfig.HeadField, out Microsoft.Extensions.Primitives.StringValues authValue))
                {
                    var authstr = authValue.ToString();
                    if (this._jwtConfig.Prefix.Length > 0)
                    {
                        authstr = authValue.ToString().Substring(this._jwtConfig.Prefix.Length+1, authValue.ToString().Length -(this._jwtConfig.Prefix.Length+1));
                    }
                    if (this._jwt.ValidateToken(authstr, out Dictionary<string, string> Clims))
                    {
                        foreach (var item in Clims)
                        {
                            context.Items.Add(item.Key, item.Value);
                        }
                        return this._next(context);
                    }
                    else
                    {
                        context.Response.StatusCode = 401;
                        context.Response.ContentType = "application/json";
                        return context.Response.WriteAsync("{\"status\":401,\"statusMsg\":\"auth vaild fail\"}");
                    }
                }
                else
                {
                    context.Response.StatusCode = 401;
                    context.Response.ContentType = "application/json";
                    return context.Response.WriteAsync("{\"status\":401,\"statusMsg\":\"auth vaild fail\"}");
                }
            }
        }
    }

7.中間件暴露出去

 public static class UseUseJwtMiddlewareExtensions
    {
        /// <summary>
        /// 權限檢查
        /// </summary>
        /// <param name="builder"></param>
        /// <returns></returns>
        public static IApplicationBuilder UseJwt(this IApplicationBuilder builder)
        {
            return builder.UseMiddleware<UseJwtMiddleware>();
        }
    }

8.在Startup.Configure中使用中間件:

 app.UseJwt(); 

 

以1的配置為例:

除了請求 /auth/getToken 不需要加頭信息外,其他的請求一律要求頭信息中必須帶着 

userless:prefix (從Auth/GetToken中獲取到的token)

再提供一個demo的下載鏈接

鏈接: https://pan.baidu.com/s/1tLpZ-HbZJPp37HQVWew8Rg 提取碼: 7n9g 

一些截圖: 

1,在需要認證的的控制器中不需要做任何操作,可以通過httpcontext.item拿到存入clims中的信息

2.startup截圖

 3.發放token

4.配置

 簡單測試

 

直接請求  無權限

 

帶着token去請求api/values得到響應

 

這里的exp就是該token的失效時間(unti時間戳),可以定義一個配置來確定什么時候要去重新生成token,這個動作在中間件中進行(比如給頭信息中帶上ReToken)客戶端下次就用ReToken中的Token重新進行訪問,很容易就做到了對token的續期操作


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM