.NET?Core支持Cookie和JWT混合認(rèn)證、授權(quán)的方法
前言
為防止JWT Token被竊取,我們將Token置于Cookie中,但若與第三方對(duì)接,調(diào)用我方接口進(jìn)行認(rèn)證、授權(quán)此時(shí)仍需將Token置于請(qǐng)求頭,通過(guò)實(shí)踐并聯(lián)系理論,我們繼續(xù)開(kāi)始整活!首先我們實(shí)現(xiàn)Cookie認(rèn)證,然后再次引入JWT,最后在結(jié)合二者使用時(shí)聯(lián)系其他我們可能需要注意的事項(xiàng)
Cookie認(rèn)證
在startup中我們添加cookie認(rèn)證服務(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;
});接下來(lái)則是使用認(rèn)證和授權(quán)中間件,注意將其置于路由和終結(jié)點(diǎn)終結(jié)點(diǎn)之間,否則啟動(dòng)也會(huì)有明確異常提示
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
......
});我們給出測(cè)試視圖頁(yè),并要求認(rèn)證即控制器添加特性
[Authorize]
public class HomeController : Controller
{
public IActionResult Index()
{
return View();
}
}當(dāng)進(jìn)入首頁(yè),未認(rèn)證默認(rèn)進(jìn)入account/login,那么接下來(lái)創(chuàng)建該視圖
public class AccountController : Controller
{
[AllowAnonymous]
public IActionResult Login()
{
return View();
}
......
}我們啟動(dòng)程序先看看效果

如上圖,自動(dòng)跳轉(zhuǎn)至登錄頁(yè),此時(shí)我們點(diǎn)擊模擬登錄按鈕,發(fā)起請(qǐng)求去模擬登錄(發(fā)起ajax請(qǐng)求代碼就占不用篇幅給出了)
/// <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();
}上述無(wú)非就是構(gòu)建身份以及該身份下所具有的身份屬性,類(lèi)似個(gè)人身份證唯一標(biāo)識(shí)個(gè)人,身份證上各個(gè)信息即表示如上聲明,同時(shí)呢,肯定要調(diào)用上下文去登錄,在整個(gè)會(huì)話未過(guò)期之前,根據(jù)認(rèn)證方案獲取對(duì)應(yīng)處理方式,最后將相關(guān)信息進(jìn)行存儲(chǔ)等等,有興趣的童鞋可以去了解其實(shí)現(xiàn)細(xì)節(jié)哈

當(dāng)我們請(qǐng)求過(guò)后,再次訪問(wèn)首頁(yè),將看到生成當(dāng)前會(huì)話信息,同時(shí)我們將會(huì)話過(guò)期設(shè)置為1分鐘,在1分鐘內(nèi)未進(jìn)行會(huì)話,將自動(dòng)重定向至登錄頁(yè),注意如上標(biāo)注并沒(méi)有值,那么這個(gè)值可以設(shè)置嗎?當(dāng)然可以,在開(kāi)始配置時(shí)我們并未給出,那么這個(gè)屬性又代表什么含義呢?
options.Cookie.MaxAge = TimeSpan.FromMinutes(2);
那么結(jié)合ExpireTimeSpan和MaxAge使用,到底代表什么意思呢?我們暫且撇開(kāi)滑動(dòng)過(guò)期設(shè)置
ExpireTimeSpan表示用戶身份認(rèn)證票據(jù)的生命周期,它是認(rèn)證cookie的有效負(fù)載,存儲(chǔ)的cookie值是一段加密字符串,在每次請(qǐng)求時(shí),web應(yīng)用程序都會(huì)根據(jù)請(qǐng)求對(duì)其進(jìn)行解密
MaxAge控制著cookie的生命周期,若cookie過(guò)期,瀏覽器將會(huì)自動(dòng)清除,如果沒(méi)有設(shè)置該值,實(shí)質(zhì)上它的生命周期就是ExpireTimeSpan,那么它到底有何意義呢?
上述我們?cè)O(shè)置票據(jù)的生命周期為1分鐘,同時(shí)我們控制cookie的生命周期為2分鐘,若在2分鐘內(nèi)關(guān)閉瀏覽器或重啟web應(yīng)用程序,此時(shí)cookie生命周期并未過(guò)期,所以仍將處于會(huì)話狀態(tài)即無(wú)需登錄,若未設(shè)置MaxAge,關(guān)閉瀏覽器或重啟后將自動(dòng)清除其值即需登錄,當(dāng)然一切前提是未手動(dòng)清除瀏覽器cookie
問(wèn)題又來(lái)了,在配置cookie選項(xiàng)中,還有一個(gè)也可以設(shè)置過(guò)期的屬性
options.Cookie.Expiration = TimeSpan.FromMinutes(3);
當(dāng)配置ExpireTimeSpan或同時(shí)配置MaxAge時(shí),無(wú)需設(shè)置Expiration,因?yàn)闀?huì)拋出異常

JWT認(rèn)證
上述已經(jīng)實(shí)現(xiàn)Cookie認(rèn)證,那么在與第三方進(jìn)行對(duì)接時(shí),我們要使用JWT認(rèn)證,我們又該如何處理呢?首先我們添加JWT認(rèn)證服務(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);
}在登錄方法中,將其寫(xiě)入響應(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是如何驗(yàn)證Token的呢?默認(rèn)是從請(qǐng)求去取Bearer Token值,若成功取到這賦值給如下context.Token,所以此時(shí)我們需要手動(dòng)從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;
}
};一切已就緒,接下來(lái)我們寫(xiě)個(gè)api接口測(cè)試驗(yàn)證看看
[Authorize("Bearer")]
[Route("api/[controller]/[action]")]
[ApiController]
public class JwtController : ControllerBase
{
[HttpGet]
public IActionResult Test()
{
return Ok("test jwt");
}
}思考一下,我們通過(guò)Postman模擬測(cè)試,會(huì)返回401嗎?結(jié)果會(huì)是怎樣的呢?

問(wèn)題不大,主要在于該特性參數(shù)為聲明指定策略,但我們需要指定認(rèn)證方案即scheme,修改成如下:

如此在與第三方對(duì)接時(shí),請(qǐng)求返回token,后續(xù)將token置于請(qǐng)求頭中即可驗(yàn)證通過(guò),同時(shí)上述取cookie中token并手動(dòng)賦值,對(duì)于對(duì)接第三方則是多余,不過(guò)是為了諸多其他原因而已
[Authorize(AuthenticationSchemes = "Bearer,Cookies")]
注意混合認(rèn)證方案設(shè)置存在順序,后者將覆蓋前者即如上設(shè)置,此時(shí)將走cookie認(rèn)證

滑動(dòng)過(guò)期思考擴(kuò)展
若我們實(shí)現(xiàn)基于Cookie滑動(dòng)過(guò)期,同時(shí)使用signalr進(jìn)行數(shù)據(jù)推送,勢(shì)必存在問(wèn)題,因?yàn)闀?huì)一直刷新會(huì)話,那么將導(dǎo)致會(huì)話永不過(guò)期問(wèn)題,從安全層面角度考慮,我們?cè)撊绾翁幚砟兀?/p>
我們知道票據(jù)生命周期存儲(chǔ)在上下文AuthenticationProperties屬性中,所以在配置Cookie選項(xiàng)事件中我們可以進(jìn)行自定義處理
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ù)時(shí),有對(duì)應(yīng)事件選項(xiàng),使用如下
options.EventsType = typeof(CookieAuthenticationEventsExetensions);
擴(kuò)展事件實(shí)現(xiàn)表示在第一次會(huì)話到當(dāng)前時(shí)間截止超過(guò)3天,則自動(dòng)重定向至登錄頁(yè),最后將上述擴(kuò)展事件進(jìn)行注冊(cè)即可
總結(jié)
暫無(wú),下次再會(huì)!
你所看到的并非事物本身,而是經(jīng)過(guò)詮釋后所賦予的意義
到此這篇關(guān)于.NET Core如何支持Cookie和JWT混合認(rèn)證、授權(quán)的文章就介紹到這了,更多相關(guān).NET Core 混合認(rèn)證、授權(quán)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- .Net?Core授權(quán)認(rèn)證方案JWT(JSON?Web?Token)初探
- ASP.NET?Core?6.0?添加?JWT?認(rèn)證和授權(quán)功能
- .Net Core實(shí)現(xiàn)JWT授權(quán)認(rèn)證
- asp.net core3.1cookie和jwt混合認(rèn)證授權(quán)實(shí)現(xiàn)多種身份驗(yàn)證方案
- ASP.NET Core使用JWT認(rèn)證授權(quán)的方法
- ASP.NET Core學(xué)習(xí)之使用JWT認(rèn)證授權(quán)詳解
- 淺談ASP.NET Core 中jwt授權(quán)認(rèn)證的流程原理
- 基于.Net?Core認(rèn)證授權(quán)方案之JwtBearer認(rèn)證
相關(guān)文章
.Net Core WebApi部署在Linux服務(wù)器上的方法
這篇文章主要介紹了.Net Core WebApi部署在Linux服務(wù)器上的方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-03-03
asp.net中CSharpThinking擴(kuò)展方法分析
這篇文章主要介紹了asp.net中CSharpThinking擴(kuò)展方法,實(shí)例講述了擴(kuò)展方法的特征及應(yīng)用,具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2014-11-11
Visual Studio 2015和 .NET Core安裝教程
這篇文章主要為大家詳細(xì)介紹了Visual Studio Community 2015和 .NET Core安裝圖文教程,感興趣的小伙伴們可以參考一下2016-07-07
ASP.NET?Core構(gòu)建OData查詢Restful?API
這篇文章主要為大家介紹了ASP.NET?Core建構(gòu)OData實(shí)體模型實(shí)現(xiàn)Restful?API查詢,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-04-04
asp.net配置會(huì)話狀態(tài)Session實(shí)現(xiàn)代碼
在Web應(yīng)用程序中,都會(huì)有一個(gè)Web.config文件來(lái)配置當(dāng)前Web項(xiàng)目。其中包括關(guān)于會(huì)話狀態(tài)Session的配置2012-10-10

