asp.net core集成JWT的步驟記錄
【什么是JWT】
JSON Web Token(JWT)是目前最流行的跨域身份驗證解決方案。
JWT的官網(wǎng)地址:https://jwt.io/
通俗地來講,JWT是能代表用戶身份的令牌,可以使用JWT令牌在api接口中校驗用戶的身份以確認用戶是否有訪問api的權(quán)限。
JWT中包含了身份認證必須的參數(shù)以及用戶自定義的參數(shù),JWT可以使用秘密(使用HMAC算法)或使用RSA或ECDSA的公鑰/私鑰對進行簽名。
【什么時候應(yīng)該使用JSON Web令牌?】
授權(quán):這是使用JWT的最常見方案。一旦用戶登錄,每個后續(xù)請求將包括JWT,允許用戶訪問該令牌允許的路由,服務(wù)和資源。Single Sign On是一種現(xiàn)在廣泛使用JWT的功能,因為它的開銷很小,并且能夠在不同的域中輕松使用。
信息交換:JSON Web令牌是在各方之間安全傳輸信息的好方法。因為JWT可以簽名 - 例如,使用公鑰/私鑰對 - 您可以確定發(fā)件人是他們所說的人。此外,由于使用標頭和有效負載計算簽名,您還可以驗證內(nèi)容是否未被篡改。
【JWT有什么優(yōu)勢?】
- 用戶向服務(wù)器發(fā)送用戶名和密碼。
- 服務(wù)器驗證通過后,在當(dāng)前對話(session)里面保存相關(guān)數(shù)據(jù),比如用戶角色、登錄時間等等。
- 服務(wù)器向用戶返回一個 session_id,寫入用戶的 Cookie。
- 用戶隨后的每一次請求,都會通過 Cookie,將 session_id 傳回服務(wù)器。
- 服務(wù)器收到 session_id,找到前期保存的數(shù)據(jù),由此得知用戶的身份。
這種模式的問題在于,擴展性(scaling)不好。單機當(dāng)然沒有問題,如果是服務(wù)器集群,或者是跨域的服務(wù)導(dǎo)向架構(gòu),就要求 session 數(shù)據(jù)共享,每臺服務(wù)器都能夠讀取 session。如果session存儲的節(jié)點掛了,那么整個服務(wù)都會癱瘓,體驗相當(dāng)不好,風(fēng)險也很高。
相比之下,JWT的實現(xiàn)方式是將用戶信息存儲在客戶端,服務(wù)端不進行保存。每次請求都把令牌帶上以校驗用戶登錄狀態(tài),這樣服務(wù)就變成了無狀態(tài)的,服務(wù)器集群也很好擴展。
【JWT令牌結(jié)構(gòu)】
在緊湊的形式中,JSON Web Tokens由dot(.)分隔的三個部分組成,它們是:
- Header 頭
- Payload 有效載荷
- Signature 簽名
因此,JWT通常如下所示:
xxxxx.yyyyy.zzzzz
1.Header 頭
標頭通常由兩部分組成:令牌的類型,即JWT,以及正在使用的簽名算法,例如HMAC SHA256或RSA。
例如:
{ "alg": "HS256", "typ": "JWT" }
然后,這個JSON被編碼為Base64Url,形成JWT的第一部分。
2.Payload有效載荷
Payload 部分也是一個 JSON 對象,用來存放實際需要傳遞的數(shù)據(jù)。JWT 規(guī)定了7個官方字段,供選用。
- iss (issuer):簽發(fā)人
- exp (expiration time):過期時間
- sub (subject):主題
- aud (audience):受眾
- nbf (Not Before):生效時間
- iat (Issued At):簽發(fā)時間
- jti (JWT ID):編號
除了官方字段,你還可以在這個部分定義私有字段,下面就是一個例子。例如:
{ "sub": "1234567890", "name": "John Doe", "admin": true }
注意,JWT 默認是不加密的,任何人都可以讀到,所以不要把秘密信息放在這個部分。這個 JSON 對象也要使用 Base64URL 算法轉(zhuǎn)成字符串。
3.Signature 簽名
Signature 部分是對前兩部分的簽名,防止數(shù)據(jù)篡改。
首先,需要指定一個密鑰(secret)。這個密鑰只有服務(wù)器才知道,不能泄露給用戶。然后,使用 Header 里面指定的簽名算法(默認是 HMAC SHA256),按照下面的公式產(chǎn)生簽名。
HMACSHA256( base64UrlEncode(header) + "." + base64UrlEncode(payload), secret)
簽名用于驗證消息在此過程中未被更改,并且,在使用私鑰簽名的令牌的情況下,它還可以驗證JWT的發(fā)件人是否是它所聲稱的人?! ?/p>
把他們?nèi)齻€全部放在一起
輸出是三個由點分隔的Base64-URL字符串,可以在HTML和HTTP環(huán)境中輕松傳遞,而與基于XML的標準(如SAML)相比更加緊湊。
下面顯示了一個JWT,它具有先前的頭和有效負載編碼,并使用機密簽名。
如果您想使用JWT并將這些概念付諸實踐,您可以使用jwt.io Debugger來解碼,驗證和生成JWT。
【JSON Web令牌如何工作?】
在身份驗證中,當(dāng)用戶使用其憑據(jù)成功登錄時,將返回JSON Web令牌。由于令牌是憑證,因此必須非常小心以防止出現(xiàn)安全問題。一般情況下,您不應(yīng)該將令牌保留的時間超過要求。
每當(dāng)用戶想要訪問受保護的路由或資源時,用戶代理應(yīng)該使用承載模式發(fā)送JWT,通常在Authorization標頭中。標題的內(nèi)容應(yīng)如下所示:
Authorization: Bearer <token>
在某些情況下,這可以是無狀態(tài)授權(quán)機制。服務(wù)器的受保護路由將檢查Authorization標頭中的有效JWT,如果存在,則允許用戶訪問受保護資源。如果JWT包含必要的數(shù)據(jù),則可以減少查詢數(shù)據(jù)庫以進行某些操作的需要,盡管可能并非總是如此。
如果在標Authorization頭中發(fā)送令牌,則跨域資源共享(CORS)將不會成為問題,因為它不使用cookie。
下圖顯示了如何獲取JWT并用于訪問API或資源:
應(yīng)用程序向授權(quán)服務(wù)器請求授權(quán)校驗用戶身份,校驗成功,返回token應(yīng)用程序使用訪問令牌訪問受保護的資源【ASP.Net Core 集成JWT】
前面我們介紹了JWT的原理,下面我們在asp.net core實際項目中集成JWT。
首先我們新建一個Demo asp.net core 空web項目
添加數(shù)據(jù)訪問模擬api,ValuesController
其中api/value1是可以直接訪問的,api/value2添加了權(quán)限校驗特性標簽 [Authorize]
using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; namespace Demo.Jwt.Controllers { [ApiController] public class ValuesController : ControllerBase { [HttpGet] [Route("api/value1")] public ActionResult<IEnumerable<string>> Get() { return new string[] { "value1", "value1" }; } [HttpGet] [Route("api/value2")] [Authorize] public ActionResult<IEnumerable<string>> Get2() { return new string[] { "value2", "value2" }; } } }
添加模擬登陸,生成Token的api,AuthController
這里模擬一下登陸校驗,只驗證了用戶密碼不為空即通過校驗,真實環(huán)境完善校驗用戶和密碼的邏輯。
using System; using System.Collections.Generic; using System.IdentityModel.Tokens.Jwt; using System.Linq; using System.Security.Claims; using System.Text; using System.Threading.Tasks; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Microsoft.IdentityModel.Tokens; namespace Demo.Jwt.Controllers { [Route("api/[controller]")] [ApiController] public class AuthController : ControllerBase { [AllowAnonymous] [HttpGet] public IActionResult Get(string userName, string pwd) { if (!string.IsNullOrEmpty(userName) && !string.IsNullOrEmpty(pwd)) { var claims = new[] { new Claim(JwtRegisteredClaimNames.Nbf,$"{new DateTimeOffset(DateTime.Now).ToUnixTimeSeconds()}") , new Claim (JwtRegisteredClaimNames.Exp,$"{new DateTimeOffset(DateTime.Now.AddMinutes(30)).ToUnixTimeSeconds()}"), new Claim(ClaimTypes.Name, userName) }; var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Const.SecurityKey)); var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256); var token = new JwtSecurityToken( issuer: Const.Domain, audience: Const.Domain, claims: claims, expires: DateTime.Now.AddMinutes(30), signingCredentials: creds); return Ok(new { token = new JwtSecurityTokenHandler().WriteToken(token) }); } else { return BadRequest(new { message = "username or password is incorrect." }); } } } }
Startup添加JWT驗證的相關(guān)配置
using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.IdentityModel.Tokens; using System; using System.Text; namespace Demo.Jwt { public class Startup { public Startup(IConfiguration configuration) { Configuration = configuration; } public IConfiguration Configuration { get; } // This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { //添加jwt驗證: services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) .AddJwtBearer(options => { options.TokenValidationParameters = new TokenValidationParameters { ValidateIssuer = true,//是否驗證Issuer ValidateAudience = true,//是否驗證Audience ValidateLifetime = true,//是否驗證失效時間 ClockSkew = TimeSpan.FromSeconds(30), ValidateIssuerSigningKey = true,//是否驗證SecurityKey ValidAudience = Const.Domain,//Audience ValidIssuer = Const.Domain,//Issuer,這兩項和前面簽發(fā)jwt的設(shè)置一致 IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Const.SecurityKey))//拿到SecurityKey }; }); services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2); } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public void Configure(IApplicationBuilder app, IHostingEnvironment env) { ///添加jwt驗證 app.UseAuthentication(); if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } app.UseMvc(routes => { routes.MapRoute( name: "default", template: "{controller=Home}/{action=Index}/{id?}"); }); } } }
最后把代碼里面用到的一些相關(guān)常量也粘貼過來,Const.cs
namespace Demo.Jwt { public class Const { /// <summary> /// 這里為了演示,寫死一個密鑰。實際生產(chǎn)環(huán)境可以從配置文件讀取,這個是用網(wǎng)上工具隨便生成的一個密鑰 /// </summary> public const string SecurityKey = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDI2a2EJ7m872v0afyoSDJT2o1+SitIeJSWtLJU8/Wz2m7gStexajkeD+Lka6DSTy8gt9UwfgVQo6uKjVLG5Ex7PiGOODVqAEghBuS7JzIYU5RvI543nNDAPfnJsas96mSA7L/mD7RTE2drj6hf3oZjJpMPZUQI/B1Qjb5H3K3PNwIDAQAB"; public const string Domain = "http://localhost:5000"; } }
到這里,已經(jīng)是我們項目的所有代碼了。
如果需要完整的項目代碼,Github地址:https://github.com/sevenTiny/Demo.Jwt
【JWT測試】
我們找一個趁手的工具,比如fiddler,然后把我們的web站點運行起來
首先調(diào)用無權(quán)限的接口:http://localhost:5000/api/value1
正確地返回了數(shù)據(jù),那么接下來我們測試JWT的流程
1. 無權(quán)限
首先我們什么都不加調(diào)用接口:http://localhost:5000/api/value2
返回了狀態(tài)碼401,也就是未經(jīng)授權(quán):訪問由于憑據(jù)無效被拒絕。 說明JWT校驗生效了,我們的接口收到了保護。
2.獲取Token
調(diào)用模擬登陸授權(quán)接口:http://localhost:5000/api/Auth?userName=zhangsan&pwd=123
這里的用戶密碼是隨便寫的,因為我們模擬登陸只是校驗了下非空,因此寫什么都能通過
成功得到了響應(yīng)
然后我們得到了一個xxx.yyy.zzz 格式的 token 值。我們把token復(fù)制出來
3.在剛才401的接口請求HEADER中添加JWT的參數(shù),把我們的token加上去
再次調(diào)用我們的模擬數(shù)據(jù)接口,但是這次我們加了一個HEADER:http://localhost:5000/api/value2
把內(nèi)容粘出來
User-Agent: Fiddler Host: localhost:5000 Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYmYiOiIxNTYwMzQ1MDIxIiwiZXhwIjoxNTYwMzQ2ODIxLCJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9uYW1lIjoiemhhbmdzYW4iLCJpc3MiOiJodHRwOi8vbG9jYWxob3N0OjUwMDAiLCJhdWQiOiJodHRwOi8vbG9jYWxob3N0OjUwMDAifQ.x7Slk4ho1hZc8sR8_McVTB6VEYLz_v-5eaHvXtIDS-o
這里需要注意Bearer 后面是有一個空格的,然后就是我們上一步獲取到的token
嗯,沒有401了,成功返回了數(shù)據(jù)
4.JWT的Token過期
我們且倒一杯開水,坐等30分鐘(我們代碼中設(shè)置的過期時間),然后再次調(diào)用數(shù)據(jù)接口:http://localhost:5000/api/value2
又變成了401,我們看下詳細的返回數(shù)據(jù)
這里有標注,錯誤描述 token過期,說明我們設(shè)置的token過期時間生效了
【結(jié)束】
到這里,我們JWT的簡介以及asp.net core 集成JWT已經(jīng)完美完成,當(dāng)然了這只是一個demo,在實際的應(yīng)用中需要補充和完善的地方還有很多。
如果想要完整項目源碼的,可以參考地址:https://github.com/sevenTiny/Demo.Jwt
如果有幸能幫助到你,高抬貴手點個star吧~
總結(jié)
以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,謝謝大家對腳本之家的支持。
- .NET?Core支持Cookie和JWT混合認證、授權(quán)的方法
- .net?core?api接口JWT方式認證Token
- ASP.NET?Core應(yīng)用JWT進行用戶認證及Token的刷新方案
- asp.net core3.1cookie和jwt混合認證授權(quán)實現(xiàn)多種身份驗證方案
- AntDesign Pro + .NET Core 實現(xiàn)基于JWT的登錄認證功能
- .Net Core官方JWT授權(quán)驗證的全過程
- 詳解ASP.NET Core Web Api之JWT刷新Token
- ASP.NET Core使用JWT認證授權(quán)的方法
- ASP.NET Core學(xué)習(xí)之使用JWT認證授權(quán)詳解
- 淺談ASP.NET Core 中jwt授權(quán)認證的流程原理
- ASP.Net Core3.0中使用JWT認證的實現(xiàn)
- .NET core 3.0如何使用Jwt保護api詳解
- .net core webapi jwt 更為清爽的認證詳解
- Asp.Net Core基于JWT認證的數(shù)據(jù)接口網(wǎng)關(guān)實例代碼
- .Net Core實現(xiàn)JWT授權(quán)認證
相關(guān)文章
asp.net開發(fā)微信派發(fā)現(xiàn)金紅包/H5網(wǎng)頁搶紅包功能(思路詳解)
這篇文章主要介紹了asp.net開發(fā)微信派發(fā)現(xiàn)金紅包/H5網(wǎng)頁搶紅包功能,本文給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2021-03-03ASP.NET MVC HttpPostedFileBase文件上傳的實例代碼
這篇文章主要介紹了ASP.NET MVC HttpPostedFileBase文件上傳,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-07-07為ASP.NET MVC及WebApi添加路由優(yōu)先級
這是一個對Asp.Net Mvc的一個很小的功能拓展,小項目可能不太需要這個功能,但有時候項目大了注冊的路由不生效時你應(yīng)該要想到有可能是因為路由順序的原因,這時這個路由優(yōu)先級的功能有可能就會給你帶來便利。2015-10-10.NET?8新預(yù)覽版使用?Blazor?組件進行服務(wù)器端呈現(xiàn)(項目體驗)
這篇文章主要介紹了.NET?8新預(yù)覽版使用?Blazor?組件進行服務(wù)器端呈現(xiàn)(項目體驗),這是 Blazor 統(tǒng)一工作的開始,旨在使 Blazor 組件能夠滿足客戶端和服務(wù)器端的所有 Web UI 需求,需要的朋友可以參考下2023-04-04