Jwt驗證登錄
練習模板://gitee.com/zh1446802857/swagger-multi-version-api.git
Jwt在我的 認知里,是一套門鎖。別人(用戶)需要用到你的介面 的時候需要通過這個身份識別才可以使用。就像是一間房子,只有有鑰匙的人才能進入。
項目開始
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服務內部)
- 添加頭部請求過濾器(添加之後會有一個頭部請求的過濾器)
- 附加許可權給所有的過濾器(給過濾器增加許可權,如果沒有對應的Token就無法通過)
- 把header添加Token且傳入後台
- 添加安全的定義來描述(初始化驗證機制)
- Type:此過濾器的安全驗證類型
- Description:描述
- In :token的位置
- 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.演示影片(不怎麼會添加本地影片,百度好像要弄個鏈接才行,能看就行~):