.NET?Core支持Cookie和JWT混合認證、授權(quán)的方法
前言
為防止JWT Token被竊取,我們將Token置于Cookie中,但若與第三方對接,調(diào)用我方接口進行認證、授權(quán)此時仍需將Token置于請求頭,通過實踐并聯(lián)系理論,我們繼續(xù)開始整活!首先我們實現(xiàn)Cookie認證,然后再次引入JWT,最后在結(jié)合二者使用時聯(lián)系其他我們可能需要注意的事項
Cookie認證
在startup中我們添加cookie認證服務(wù),如下:
services.AddAuthentication(options => { options.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme; options.DefaultChallengeScheme = CookieAuthenticationDefaults.AuthenticationScheme; }) .AddCookie(options => { options.ExpireTimeSpan = TimeSpan.FromMinutes(1); options.Cookie.Name = "user-session"; options.SlidingExpiration = true; });
接下來則是使用認證和授權(quán)中間件,注意將其置于路由和終結(jié)點終結(jié)點之間,否則啟動也會有明確異常提示
app.UseRouting(); app.UseAuthentication(); app.UseAuthorization(); app.UseEndpoints(endpoints => { ...... });
我們給出測試視圖頁,并要求認證即控制器添加特性
[Authorize] public class HomeController : Controller { public IActionResult Index() { return View(); } }
當(dāng)進入首頁,未認證默認進入account/login,那么接下來創(chuàng)建該視圖
public class AccountController : Controller { [AllowAnonymous] public IActionResult Login() { return View(); } ...... }
我們啟動程序先看看效果
如上圖,自動跳轉(zhuǎn)至登錄頁,此時我們點擊模擬登錄按鈕,發(fā)起請求去模擬登錄(發(fā)起ajax請求代碼就占不用篇幅給出了)
/// <summary> /// 模擬登錄 /// </summary> /// <returns></returns> [HttpPost] [AllowAnonymous] public async Task<IActionResult> TestLogin() { var claims = new Claim[] { new Claim(ClaimTypes.Name, "Jeffcky"), }; var claimsIdentity = new ClaimsIdentity(claims, "Login"); await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, new ClaimsPrincipal(claimsIdentity)); return Ok(); }
上述無非就是構(gòu)建身份以及該身份下所具有的身份屬性,類似個人身份證唯一標(biāo)識個人,身份證上各個信息即表示如上聲明,同時呢,肯定要調(diào)用上下文去登錄,在整個會話未過期之前,根據(jù)認證方案獲取對應(yīng)處理方式,最后將相關(guān)信息進行存儲等等,有興趣的童鞋可以去了解其實現(xiàn)細節(jié)哈
當(dāng)我們請求過后,再次訪問首頁,將看到生成當(dāng)前會話信息,同時我們將會話過期設(shè)置為1分鐘,在1分鐘內(nèi)未進行會話,將自動重定向至登錄頁,注意如上標(biāo)注并沒有值,那么這個值可以設(shè)置嗎?當(dāng)然可以,在開始配置時我們并未給出,那么這個屬性又代表什么含義呢?
options.Cookie.MaxAge = TimeSpan.FromMinutes(2);
那么結(jié)合ExpireTimeSpan和MaxAge使用,到底代表什么意思呢?我們暫且撇開滑動過期設(shè)置
ExpireTimeSpan表示用戶身份認證票據(jù)的生命周期,它是認證cookie的有效負載,存儲的cookie值是一段加密字符串,在每次請求時,web應(yīng)用程序都會根據(jù)請求對其進行解密
MaxAge控制著cookie的生命周期,若cookie過期,瀏覽器將會自動清除,如果沒有設(shè)置該值,實質(zhì)上它的生命周期就是ExpireTimeSpan,那么它到底有何意義呢?
上述我們設(shè)置票據(jù)的生命周期為1分鐘,同時我們控制cookie的生命周期為2分鐘,若在2分鐘內(nèi)關(guān)閉瀏覽器或重啟web應(yīng)用程序,此時cookie生命周期并未過期,所以仍將處于會話狀態(tài)即無需登錄,若未設(shè)置MaxAge,關(guān)閉瀏覽器或重啟后將自動清除其值即需登錄,當(dāng)然一切前提是未手動清除瀏覽器cookie
問題又來了,在配置cookie選項中,還有一個也可以設(shè)置過期的屬性
options.Cookie.Expiration = TimeSpan.FromMinutes(3);
當(dāng)配置ExpireTimeSpan或同時配置MaxAge時,無需設(shè)置Expiration,因為會拋出異常
JWT認證
上述已經(jīng)實現(xiàn)Cookie認證,那么在與第三方進行對接時,我們要使用JWT認證,我們又該如何處理呢?首先我們添加JWT認證服務(wù)
.AddJwtBearer(options => { options.TokenValidationParameters = new TokenValidationParameters { ValidateIssuerSigningKey = true, IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("1234567890123456")), ValidateIssuer = true, ValidIssuer = "http://localhost:5000", ValidateAudience = true, ValidAudience = "http://localhost:5001", ValidateLifetime = true, ClockSkew = TimeSpan.FromMinutes(5) }; });
將JWT Token置于cookie中,此前文章已有講解,這里我們直接給出代碼,先生成Token
private string GenerateToken(Claim[] claims) { var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("1234567890123456")); var token = new JwtSecurityToken( issuer: "http://localhost:5000", audience: "http://localhost:5001", claims: claims, notBefore: DateTime.Now, expires: DateTime.Now.AddMinutes(5), signingCredentials: new SigningCredentials(key, SecurityAlgorithms.HmacSha256) ); return new JwtSecurityTokenHandler().WriteToken(token); }
在登錄方法中,將其寫入響應(yīng)cookie中,如下這般
/// <summary> /// 模擬登錄 /// </summary> /// <returns></returns> [HttpPost] [AllowAnonymous] public async Task<IActionResult> TestLogin() { var claims = new Claim[] { new Claim(ClaimTypes.Name, "Jeffcky"), }; var claimsIdentity = new ClaimsIdentity(claims, "Login"); Response.Cookies.Append("x-access-token", GenerateToken(claims), new CookieOptions() { Path = "/", HttpOnly = true }); await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, new ClaimsPrincipal(claimsIdentity)); return Ok(); }
那么JWT是如何驗證Token的呢?默認是從請求去取Bearer Token值,若成功取到這賦值給如下context.Token,所以此時我們需要手動從cookie中取出token并賦值
options.Events = new JwtBearerEvents { OnMessageReceived = context => { var accessToken = context.Request.Cookies["x-access-token"]; if (!string.IsNullOrEmpty(accessToken)) { context.Token = accessToken; } return Task.CompletedTask; } };
一切已就緒,接下來我們寫個api接口測試驗證看看
[Authorize("Bearer")] [Route("api/[controller]/[action]")] [ApiController] public class JwtController : ControllerBase { [HttpGet] public IActionResult Test() { return Ok("test jwt"); } }
思考一下,我們通過Postman模擬測試,會返回401嗎?結(jié)果會是怎樣的呢?
問題不大,主要在于該特性參數(shù)為聲明指定策略,但我們需要指定認證方案即scheme,修改成如下:
如此在與第三方對接時,請求返回token,后續(xù)將token置于請求頭中即可驗證通過,同時上述取cookie中token并手動賦值,對于對接第三方則是多余,不過是為了諸多其他原因而已
[Authorize(AuthenticationSchemes = "Bearer,Cookies")]
注意混合認證方案設(shè)置存在順序,后者將覆蓋前者即如上設(shè)置,此時將走cookie認證
滑動過期思考擴展
若我們實現(xiàn)基于Cookie滑動過期,同時使用signalr進行數(shù)據(jù)推送,勢必存在問題,因為會一直刷新會話,那么將導(dǎo)致會話永不過期問題,從安全層面角度考慮,我們該如何處理呢?
我們知道票據(jù)生命周期存儲在上下文AuthenticationProperties屬性中,所以在配置Cookie選項事件中我們可以進行自定義處理
public class CookieAuthenticationEventsExetensions : CookieAuthenticationEvents { private const string TicketIssuedTicks = nameof(TicketIssuedTicks); public override async Task SigningIn(CookieSigningInContext context) { context.Properties.SetString( TicketIssuedTicks, DateTimeOffset.UtcNow.Ticks.ToString()); await base.SigningIn(context); } public override async Task ValidatePrincipal( CookieValidatePrincipalContext context) { var ticketIssuedTicksValue = context .Properties.GetString(TicketIssuedTicks); if (ticketIssuedTicksValue is null || !long.TryParse(ticketIssuedTicksValue, out var ticketIssuedTicks)) { await RejectPrincipalAsync(context); return; } var ticketIssuedUtc = new DateTimeOffset(ticketIssuedTicks, TimeSpan.FromHours(0)); if (DateTimeOffset.UtcNow - ticketIssuedUtc > TimeSpan.FromDays(3)) { await RejectPrincipalAsync(context); return; } await base.ValidatePrincipal(context); } private static async Task RejectPrincipalAsync( CookieValidatePrincipalContext context) { context.RejectPrincipal(); await context.HttpContext.SignOutAsync(); } }
在添加Cookie服務(wù)時,有對應(yīng)事件選項,使用如下
options.EventsType = typeof(CookieAuthenticationEventsExetensions);
擴展事件實現(xiàn)表示在第一次會話到當(dāng)前時間截止超過3天,則自動重定向至登錄頁,最后將上述擴展事件進行注冊即可
總結(jié)
暫無,下次再會!
你所看到的并非事物本身,而是經(jīng)過詮釋后所賦予的意義
到此這篇關(guān)于.NET Core如何支持Cookie和JWT混合認證、授權(quán)的文章就介紹到這了,更多相關(guān).NET Core 混合認證、授權(quán)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- .Net?Core授權(quán)認證方案JWT(JSON?Web?Token)初探
- ASP.NET?Core?6.0?添加?JWT?認證和授權(quán)功能
- .Net Core實現(xiàn)JWT授權(quán)認證
- asp.net core3.1cookie和jwt混合認證授權(quán)實現(xiàn)多種身份驗證方案
- ASP.NET Core使用JWT認證授權(quán)的方法
- ASP.NET Core學(xué)習(xí)之使用JWT認證授權(quán)詳解
- 淺談ASP.NET Core 中jwt授權(quán)認證的流程原理
- 基于.Net?Core認證授權(quán)方案之JwtBearer認證
相關(guān)文章
使用Fiddler調(diào)試visual studion多個虛擬站點的問題分析
本篇文章小編為大家介紹,使用Fiddler調(diào)試visual studion多個虛擬站點的問題分析。需要的朋友參考下2013-04-04國產(chǎn)化之Arm64?CPU+銀河麒麟系統(tǒng)安裝.NetCore的步驟詳解
這篇文章主要介紹了國產(chǎn)化之Arm64?CPU+銀河麒麟系統(tǒng)安裝.NetCore,這里就以ARM架構(gòu)舉例,其它CPU平臺的安裝過程都一樣,要下載的包不同而已,感興趣的朋友跟隨小編一起看看吧2022-03-03擴展ASP.NET MVC三層框架且使用StructureMap實現(xiàn)依賴注入1-Model層
本篇文章將向大家介紹如何添加Service和Repository層并且使用StructureMap把Service層注入到Controller,把Repository注入到Service層。2013-04-04詳解ASP.NET MVC 解析模板生成靜態(tài)頁(RazorEngine)
我們在很多項目開發(fā)中會常常用到頁面靜態(tài)化,本篇文章主要介紹了詳解ASP.NET MVC 解析模板生成靜態(tài)頁(RazorEngine) ,具有一定的參考價值,有興趣的可以了解一下。2017-03-03ASP.NET中實現(xiàn)文件的保護性下載基礎(chǔ)篇
許多時候,我們需要在因特網(wǎng)上提供文件下載服務(wù),但是又要防止未經(jīng)授權(quán)的下載,這時該怎么辦?本文將為讀者詳細介紹一種使用ASP.NET實現(xiàn)的HTTP處理程序的解決方案。2011-02-02.NET Framework集成Quartz的實現(xiàn)示例
本文主要介紹了.NET Framework集成Quartz的實現(xiàn)示例,Quartz 主要用于定時執(zhí)行任務(wù)方面,文中通過示例代碼介紹的非常詳細,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2024-03-03