C# JWT權限驗證


JWT(Json Web Token)
什么是JWT,它是一種對API的保護方案,為什么要進行保護呢
  1. 防泄漏:你肯定不希望你的數據能被別人隨意調用,比如公司的機密信息,不可能每個人都可以訪問到
  2. 防攻擊:防止被人偽裝惡意調用接口,利用網關就把請求攔截在外面,防止對服務器造成資源壓力
  3. 防止被人篡改,導致請求不到信息,防重放攻擊(案例:在公共網絡環境中,請求被截獲,稍后被重放或多次重放)
設計原則
  1. 輕量級
  2. 易於開發、測試和部署
  3. 適合於異構系統(跨操作系統、多語言簡易實現)
  4. 所有寫操作接口(增、刪、改 操作)
  5. 非公開的讀接口(如:涉密/敏感/隱私 等)
JWT核心知識
claim:claim就是聲明,就像身份證上的地址,個人信息
Httpcontext是如何通過claim鑒權的:我們去辦業務的時候,需要出示自己的身份證,這個身份證就相當於自己的令牌進行訪問
JWT由三部分組成
第一部分是頭部,頭部分為兩部分( Header
  1. 算法、聲明
  2. 類型type:JWT類型
第二部分是載荷相當於HTML中的body( Payload
第三部分就是簽名也是就是密鑰( Signature
基於.NetCore WebApi創建一個簡單的token令牌
public ActionResult<IEnumerable<string>>Get()
{
//創建聲明Token數組
var claim =newClaim[]
{
newClaim(ClaimTypes.Name,"ylc"),
newClaim(JwtRegisteredClaimNames.Email,"1234567@11.com"),
newClaim(JwtRegisteredClaimNames.Sub,"Sub")
};
//實例化一個token對象
var token =newJwtSecurityToken(claims:claim);
//生成token
var jwtToken =newJwtSecurityTokenHandler().WriteToken(token);
returnnewstring[]{ jwtToken };
}
啟動項目之后我們會得到一個token令牌
粘貼到jwt官網中可以解密,下面介紹了另一種解密方法
token令牌分為兩部分,紅色部分解析出來代表頭部,粉色代表載荷,我們探入的信息
雖然得到令牌,但是頭部的加密算法顯示none,也沒有數字簽名,所以下面顯示無效簽名
到底這個加密算法怎么配置呢,接下來進行擴展
通過查看實例化token的函數發現它有五個重寫的構造函數
 
           
publicJwtSecurityToken(string issuer =null, string audience =null, IEnumerable<Claim> claims =null, DateTime? notBefore =default(DateTime?), DateTime? expires =default(DateTime?), SigningCredentials signingCredentials =null)
{
if(expires.HasValue && notBefore.HasValue && notBefore >= expires)
{
throw LogHelper.LogExceptionMessage(newArgumentException(LogHelper.FormatInvariant("IDX12401: Expires: '{0}' must be after NotBefore: '{1}'.", expires.Value, notBefore.Value)));
}
            Payload =newJwtPayload(issuer, audience, claims, notBefore, expires);
            Header =newJwtHeader(signingCredentials);
            RawSignature = string.Empty;
}

 我們就聲明五個參數

[HttpGet]
public ActionResult<IEnumerable<string>>Get()
{
//創建聲明Token數組
var claim =newClaim[]
{
newClaim(ClaimTypes.Name,"ylc"),
newClaim(JwtRegisteredClaimNames.Email,"1234567@11.com"),
newClaim(JwtRegisteredClaimNames.Sub,"Sub")
 
};
var key =newSymmetricSecurityKey(Encoding.UTF8.GetBytes("yanglingcong@qq.com"));//密鑰大小要超過128bt,最少要16位
 
//實例化一個token對象
var token =newJwtSecurityToken(
                issuer:"http://localhost:5000",//發起人:當前項目
                audience:"http://localhost:5000",//訂閱:我們需要誰去使用這個Token
                claims: claim,//聲明的數組
                expires:DateTime.Now.AddDays(1),//當前時間加一小時,一小時后過期
                signingCredentials:newSigningCredentials(key,SecurityAlgorithms.HmacSha256)//數字簽名 第一部分是密鑰,第二部分是加密方式
);
var jwtToken =newJwtSecurityTokenHandler().WriteToken(token);
returnnewstring[]{ jwtToken };
}
可能會出現一下報錯:IDX10603: Decryption failed. Keys tried: '[PII is hidden]'. Exceptions caught: '[PII is hidden]'. token: '[PII is hidden]' Parameter name: KeySize
安裝最新版 Microsoft.IdentityModel.Logging nuget包
最后成功運行,加密算法已經顯示出 HS256
但是最下方還是顯示數字簽名無效,還要怎么做呢
我們對令牌解密不一定要看官網通過瀏覽器控制台打印出來也可以
將令牌的一部分放入atob中就可以進行解密
相信做完這些對JWT的三大部分有了一定的了解
聲明Claim的兩種方式
開始在聲明Claim的時候用到了ClaimTypes和JwtRegisteredClaimNames這兩種
var claim =newClaim[]
{
newClaim(ClaimTypes.Name,"ylc"),
newClaim(JwtRegisteredClaimNames.Email,"1234567@11.com"),
newClaim(JwtRegisteredClaimNames.Sub,"Sub")
};
F12查看ClaimTypes源碼里面寫的都是.net Core中提供的常用的靜態變量
查看JwtRegisteredClaimNames源碼都是JWT自己提供的
但是可能有人想獲取令牌中的Email的值,獲取不到
接下來就說到獲取令牌的三種方式,在這之前要使用http進行上下文注入
[HttpGet("{str}")]
public ActionResult<IEnumerable<string>>Get(string str)
{
///獲取Token的三種方式
 
//第一種直接用JwtSecurityTokenHandler提供的read方法
var jwtHander =newJwtSecurityTokenHandler();
            JwtSecurityToken jwtSecurityToken = jwtHander.ReadJwtToken(str);
 
//第二種 通過User對象獲取
var sub = User.FindFirst(d => d.Type =="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name")?.Value;
 
//第三種 通過Httpcontext上下文中直接獲取
var name = _accessor.HttpContext.User.Identity.Name;
var Claims = _accessor.HttpContext.User.Claims;
var claimstype =(from item in Claims where item.Type == JwtRegisteredClaimNames.Email select item.Value).ToList();
 
returnnewstring[]{ JsonConvert.SerializeObject(jwtSecurityToken), sub, name, JsonConvert.SerializeObject(claimstype)};
}
然后在postman進行測試,路徑后面傳入Token令牌,傳入的Token的string參數實際是為了方法一,第二三種是沒有調用str的,所以它們為空跟Token沒有任何關系
除了第一種JwtSecurityTokenHandler獲取的值不為空,其他都為空這是為什么呢
剛剛只是生成了令牌並沒有出現內存中,也就是在http上下文中,下面就是要說到HttpContext是如何鑒權的
首先我們必須在服務里注冊一個Bealer認證,也就是說我們必須注冊一個認證才可以獲取內容
如何進行 Jwt Bealer認證呢,需要在配置服務的做相應的配置
之前在聲明Token的時候指明了發起人訂閱人,這里也要對應上,就相當於解密的過程
publicvoidConfigureServices(IServiceCollection services)
{
            services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
            services.AddSingleton<IHttpContextAccessor,HttpContextAccessor>();
 
var keyBase64 ="yanglingcong@qq.com";
var keyByeArray = Encoding.ASCII.GetBytes(keyBase64);
var signkey =newSymmetricSecurityKey(keyByeArray);
 
var tokenValidationParameters =newTokenValidationParameters//生成了一個Token驗證的參數
{
                ValidateIssuerSigningKey =true,
                IssuerSigningKey = signkey,//數字簽名
                ValidateIssuer =true,
                ValidIssuer ="http://localhost:5000",//發行人
                ValidateAudience =true,
                ValidAudience ="http://localhost:5000",//訂閱人
                ValidateLifetime =true,
                ClockSkew = TimeSpan.FromSeconds(30),
                RequireExpirationTime =true,
};
 
            services.AddAuthentication("Bearer")//注入服務  1.開啟Bearer認證 2.注冊Bearer服務
.AddJwtBearer(o =>
{
                 o.TokenValidationParameters = tokenValidationParameters;
});
}
完成Bearer以后,我們再進行postman測試,第二種第三種還是為空,這是什么原因呢
我們通過三步創建令牌之后,通過注入完成我們的認證,還需要開啟中間件管道,如果不開啟的話,Token令牌還是不能傳送到HttpContext上下文,中間件實際就是操作Http上下文。
 app.UseAuthentication();//開啟中間件

還差最后一步就是登錄,只有登錄認證了Bearer才能將Token轉化成相應的服務

服務對象在經過中間件的時候添加到HttpContext中,然后賦值給User
 
通過登錄生成Token令牌,返回給客戶端,客戶端拿着Token,放在header里面,傳輸給服務器進行校驗,校驗成功返回數據
返回了三個參數,最后一個還是空
通過項目調試,發現我們之前的Email參數被改名了,原本是JwtRegisteredClaimNames變成了ClaimTypes的聲明方法,這就需要解除,.NetCore自己的映射匹配,使用JWT,在ConfigureServices加上
 JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();//把 JwtSecurityToken清除
顯示成功
我們在當前GET方法中進行測試加上 [Authorize],把 Authorization取消勾選 運行
得到401無狀態,我們再把鈎點上,成功授權
 
 

 


免責聲明!

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



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