Jwt驗證登錄

練習模板://gitee.com/zh1446802857/swagger-multi-version-api.git


Jwt在我的 認知里,是一套門鎖。別人(用戶)需要用到你的介面 的時候需要通過這個身份識別才可以使用。就像是一間房子,只有有鑰匙的人才能進入。

項目開始

1新建一個類庫(可復用)

  1. 新建一個Molde類,包含你的Token所攜帶的資訊,例如我的:TokenModel
    using System;
    
    namespace JwtCommon
    {
        /// <summary>
        /// Toekn令牌實體包含你所攜帶的Token資訊
        /// 作為登陸,我們包含帳號,姓名,角色,密碼即可
        /// </summary>
        public class TokenModel
        {
            /// <summary>
            /// ID
            /// </summary>
            public int Id { get; set; }
            /// <summary>
            /// 姓名
            /// </summary>
            public string Name { get; set; }
            /// <summary>
            /// 角色
            /// </summary>
            public string Role { get; set; }
            /// <summary>
            /// 密碼
            /// </summary>
            public string Pass { get; set; }
            /// <summary>
            /// 發行人
            /// </summary>
            public string iss { get; set; }
            /// <summary>
            /// 訂閱人
            /// </summary>
            public string aud { get; set; }
            /// <summary>
            /// 密鑰
            /// </summary>
            public string key { get; set; }
        }
    }

獲取Token  :假如我們要回家,要用鑰匙打開門才能進去,不然就會攔在門外。與之相對應的,jwt驗證。程式要調用某個介面的時候,要有一個”鑰匙」即Token令牌     
創建一個方法實現創建Token功能   
引入Gti包:1:Microsoft.EXtensions.Confoguration 構造函數讀取配置資訊2:System.IdentityModel.Tokens.Jwt 對jwt操作
有三個參數會在多個地方使用,且應保持一致,因此將其寫在配置文件當中 issuer(發行人),audience(訂閱人),key(密鑰:簽署證書)

 "JWT": {
    "iss": "NetCoreApi",//發行人(此項目)
    "aud": "EveryOne",//訂閱人(所有人)
    "key": "IAmTheMostHandsomeInTheWorld"//秘鑰(16位+)
  }

 

CreateToken

public class JwtHelper
    {
       public string CreateToken(TokenModel tokenModel)
        {
            var claims = new List<Claim>()
            {
                new Claim(JwtRegisteredClaimNames.Jti,tokenModel.Id.ToString()),//Jti(Jwt Id,唯一標識)
                new Claim(JwtRegisteredClaimNames.Iss,tokenModel.iss),
                new Claim(JwtRegisteredClaimNames.Aud,tokenModel.aud),
                //nbf(not before)可以理解為:Token生效的時間,在你設定的生效時間之前Token是無效的
                new Claim(JwtRegisteredClaimNames.Nbf,$"{new DateTimeOffset(DateTime.Now).ToUnixTimeSeconds()}"),
                //exp(expiration time)過期時間,當前時間+你設置的過期時間
                new Claim(JwtRegisteredClaimNames.Exp,$"{new DateTimeOffset(DateTime.Now.AddMinutes(1)).ToUnixTimeSeconds()}"),
                //jwt發行時間,可以獲取jwt年齡(能知道jwt什麼吧                                                    時候開始工作的)
                new Claim(JwtRegisteredClaimNames.Iat,$"{new DateTimeOffset(DateTime.Now).ToUnixTimeSeconds()}"),
                new Claim(ClaimTypes.Name,tokenModel.Name)//姓名
            };
            //假設有多個角色,批量添加(將role切割成多個角色,查詢出每一個角色添加到claims中去)
            claims.AddRange(tokenModel.Role.Split(',').Select(a => new Claim(ClaimTypes.Role, a)));

            //設置密鑰
            var key68 = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(tokenModel.key));
            var keycode = new SigningCredentials(key68, SecurityAlgorithms.HmacSha256);
            var jwt = new JwtSecurityToken(issuer: tokenModel.iss, claims: claims, signingCredentials: keycode);
            var JwtToken = new JwtSecurityTokenHandler().WriteToken(jwt);
            return JwtToken;
        }
    }

我們去控制器里調用這個方法,來得到Token,我的控制器叫EatDinnerController

CreateToken

#region [構造]
        ///構造函數是為了得到Appsettinggs.json里的配置資訊
        public IConfiguration _configuration { get; }
        public EatDinnerController(IConfiguration configuration)
        {
            _configuration = configuration;
        }
        #endregion

        #region [全局變數]
        JwtHelper _jwtHelper = new JwtHelper();
        #endregion
        
        /// <summary>
        /// 獲取Token令牌
        /// </summary>
        /// <param name="name">姓名</param>
        /// <param name="pass">密碼</param>
        /// <returns>Token令牌</returns>
        [HttpGet]
        [Route("GetToken")]
        public string GetToken(string name, int pass)
        {
            //從配置資訊讀取ISS,AUD,KEY
            var iss = _configuration["JWT:iss"];
            var aud = _configuration["JWT:aud"];
            var key = _configuration["JWT:key"];
            return _jwtHelper.CreateToken(new TokenModel
            {
                aud = aud,
                Id = (new Random().Next(10) + 1),//沒有連接資料庫,Id先隨機任意的數字吧
                iss = iss,
                key = key,
                Name = name,
                Pass = pass.ToString(),
                Role = "Admin,User,Jack"
            });
        }

 

顯示效果顯示效果

運行結果: 
Response body裡面的一長串字元就是我們要的結果eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiIxMCIsImlzcyI6Ik5ldENvcmVBcGkiLCJhdWQiOiJFdmVyeU9uZSIsIm5iZiI6IjE2NTA3MDk5NjUiLCJleHAiOiIxNjUwNzEwMDI1IiwiaWF0IjoiMTY1MDcwOTk2NSIsImh0dHA6Ly9zY2hlbWFzLnhtbHNvYXAub3JnL3dzLzIwMDUvMDUvaWRlbnRpdHkvY2xhaW1zL25hbWUiOiLlvKDml6DmnoEiLCJodHRwOi8vc2NoZW1hcy5taWNyb3NvZnQuY29tL3dzLzIwMDgvMDYvaWRlbnRpdHkvY2xhaW1zL3JvbGUiOlsiQWRtaW4iLCJVc2VyIiwiSmFjayJdfQ.Cleaf-WuUhGUjYgPWhd7x7XVcCyqRhYvhf1vzsmRCBQ
拿到官網解析看看:紅色對應程式碼里的加密類型及方案,紫色對應程式碼里的claims,藍色則是密鑰:IAmTheMostHandsomeInTheWorld

解析結果

到這裡相當於我們已將把鎖製造出來了,但是還沒有將鎖裝到門上面,且我們使用的是swagger介面文檔,則需引入一個get包SwashBuckle.AspNetCore.Filters

  隨後應該在StartUp.cs 中ConfigureServices中的AddSwaggerGen服務中添加程式碼(Swagger服務內部

  1. 添加頭部請求過濾器(添加之後會有一個頭部請求的過濾器)
  2. 附加許可權給所有的過濾器(給過濾器增加許可權,如果沒有對應的Token就無法通過)
  3. 把header添加Token且傳入後台 
  4. 添加安全的定義來描述(初始化驗證機制)
  1. Type:此過濾器的安全驗證類型
  2. Description:描述
  3. In :token的位置
  4. Name:header傳入Token對應的參數名
services.AddSwaggerGen(s =>
            {
                //OperationFilter操作過濾器(驗證)
                //AddResponseHeadersFilter添加頭部請求過濾器
                //AppendAuthorizeToSummaryOperationFilter附加許可權
                s.OperationFilter<AddResponseHeadersFilter>();
                s.OperationFilter<AppendAuthorizeToSummaryOperationFilter>();
                //在header中添加Token傳遞給後台
                //SecurityRequirementsOperationFilter安全所需的
                s.OperationFilter<SecurityRequirementsOperationFilter>();
                //創建一個或者多個Security Definition(安全定義)描述你的api如何被保護的
                s.AddSecurityDefinition("oauth2", new OpenApiSecurityScheme
                {
                    Type = SecuritySchemeType.ApiKey,//安全方案/類型(模式)APIkey
                    Description = "Jwt授權(數據在請求頭中進行傳輸) \n 請輸入 Bearer {你的token(無需加括弧,Bearer+空格+Token)}",
                    In = ParameterLocation.Header,//In Api密鑰的位置(即通過什麼傳輸的——頭部傳輸)
                    Name = "JwtAuthoriza"
                });
            });

 效果:

【此時,我們將鎖添加在了門上】 

這時候還需要一個驗證鑰匙的配置,即Bearer認證:雖然我們有鎖有鑰匙(Token),但是在程式碼層次,還需要配置認證服務才能識別出Token的持有者身份,即鑒權(鑒定許可權)


假如我們不加入認證:

No authenticationScheme was specified, and there was no DefaultChallengeScheme found.
譯文:未指定authenticationScheme,也未找到DefaultChallengeScheme。

引入Get包:Microsoft.AspNetCore.Authentication.JwtBearer

依然是ConfigureServices方法//統一認證

 

統一的Bearer身份認證


 //統一Bearer授權認證
            services.AddAuthentication(s =>
            {
                //No authenticationScheme was specified, and there was no DefaultChallengeScheme found.
                //不寫驗證的時候剛才出現的兩個單詞,需要設置一下默認值先
                s.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
                s.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
            }).AddJwtBearer(b =>
            {
                #region 【獲取配置資訊】
                var iss = Configuration["JWT:iss"];
                var aud = Configuration["JWT:aud"];
                var key = Configuration["JWT:key"];
                //設置密鑰
                var key68 = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(key));
                var keycode = new SigningCredentials(key68, SecurityAlgorithms.HmacSha256);
                #endregion

                b.TokenValidationParameters = new TokenValidationParameters
                {
                    ValidateIssuerSigningKey = true,//是否驗證對安全令牌簽名的密鑰驗證(獲取Token的時候設置了key)
                    IssuerSigningKey = key68,//獲取或設置用於簽名驗證的KEY
                    ValidateAudience = true,
                    ValidateIssuer = true,
                    ValidIssuer = iss,
                    ValidAudience = aud,
                    ValidateLifetime = true,//驗證有效期
                    ClockSkew = TimeSpan.Zero,//時間偏移,可設置0
                    RequireExpirationTime = true//獲取或設置一個值,表示令牌是否必須擁有有效期
                };
            });

 現在,鑰匙有了。鎖有了。驗證鑰匙和鎖是否配對的方法也有了,現在只需要把鑰匙插進去就OK了

配置官方認證中間件

app.UseRouting();
//一定要保持在UseRouting下面且順序正確
app.UseAuthentication();//開啟驗證
app.UseAuthorization();//開啟授權

效果演示:

1.首先不驗證介面不會出現很長的錯誤,只會顯示401異常,表示未驗證。

 

 2.演示影片(不怎麼會添加本地影片,百度好像要弄個鏈接才行,能看就行~):

 

Tags: