.NET Core怎么实现Cookie和JWT混合认证、授权
.NET Core怎么实现Cookie和JWT混合认证、授权
本篇内容介绍了“.NETCore怎么实现Cookie和JWT混合认证、授权”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!
前言
为防止JWT Token被窃取,我们将Token置于Cookie中,但若与第三方对接,调用我方接口进行认证、授权此时仍需将Token置于请求头,通过实践并联系理论,我们继续开始整活!首先我们实现Cookie认证,然后再次引入JWT,最后在结合二者使用时联系其他我们可能需要注意的事项
Cookie认证
在startup中我们添加cookie认证服务,如下:
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;});
接下来则是使用认证和授权中间件,注意将其置于路由和终结点终结点之间,否则启动也会有明确异常提示
app.UseRouting();app.UseAuthentication();app.UseAuthorization();app.UseEndpoints(endpoints=>{......});
我们给出测试视图页,并要求认证即控制器添加特性
[Authorize]publicclassHomeController:Controller{publicIActionResultIndex(){returnView();}}
当进入首页,未认证默认进入account/login,那么接下来创建该视图
publicclassAccountController:Controller{[AllowAnonymous]publicIActionResultLogin(){returnView();}......}
我们启动程序先看看效果
如上图,自动跳转至登录页,此时我们点击模拟登录按钮,发起请求去模拟登录(发起ajax请求代码就占不用篇幅给出了)
///<summary>///模拟登录///</summary>///<returns></returns>[HttpPost][AllowAnonymous]publicasyncTask<IActionResult>TestLogin(){varclaims=newClaim[]{newClaim(ClaimTypes.Name,"Jeffcky"),};varclaimsIdentity=newClaimsIdentity(claims,"Login");awaitHttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme,newClaimsPrincipal(claimsIdentity));returnOk();}
上述无非就是构建身份以及该身份下所具有的身份属性,类似个人身份证唯一标识个人,身份证上各个信息即表示如上声明,同时呢,肯定要调用上下文去登录,在整个会话未过期之前,根据认证方案获取对应处理方式,最后将相关信息进行存储等等,有兴趣的童鞋可以去了解其实现细节哈
当我们请求过后,再次访问首页,将看到生成当前会话信息,同时我们将会话过期设置为1分钟,在1分钟内未进行会话,将自动重定向至登录页,注意如上标注并没有值,那么这个值可以设置吗?当然可以,在开始配置时我们并未给出,那么这个属性又代表什么含义呢?
options.Cookie.MaxAge=TimeSpan.FromMinutes(2);
那么结合ExpireTimeSpan和MaxAge使用,到底代表什么意思呢?我们暂且撇开滑动过期设置
ExpireTimeSpan表示用户身份认证票据的生命周期,它是认证cookie的有效负载,存储的cookie值是一段加密字符串,在每次请求时,web应用程序都会根据请求对其进行解密
MaxAge控制着cookie的生命周期,若cookie过期,浏览器将会自动清除,如果没有设置该值,实质上它的生命周期就是ExpireTimeSpan,那么它到底有何意义呢?
上述我们设置票据的生命周期为1分钟,同时我们控制cookie的生命周期为2分钟,若在2分钟内关闭浏览器或重启web应用程序,此时cookie生命周期并未过期,所以仍将处于会话状态即无需登录,若未设置MaxAge,关闭浏览器或重启后将自动清除其值即需登录,当然一切前提是未手动清除浏览器cookie
问题又来了,在配置cookie选项中,还有一个也可以设置过期的属性
options.Cookie.Expiration=TimeSpan.FromMinutes(3);
当配置ExpireTimeSpan或同时配置MaxAge时,无需设置Expiration,因为会抛出异常
JWT认证
上述已经实现Cookie认证,那么在与第三方进行对接时,我们要使用JWT认证,我们又该如何处理呢?首先我们添加JWT认证服务
.AddJwtBearer(options=>{options.TokenValidationParameters=newTokenValidationParameters{ValidateIssuerSigningKey=true,IssuerSigningKey=newSymmetricSecurityKey(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
privatestringGenerateToken(Claim[]claims){varkey=newSymmetricSecurityKey(Encoding.UTF8.GetBytes("1234567890123456"));vartoken=newJwtSecurityToken(issuer:"http://localhost:5000",audience:"http://localhost:5001",claims:claims,notBefore:DateTime.Now,expires:DateTime.Now.AddMinutes(5),signingCredentials:newSigningCredentials(key,SecurityAlgorithms.HmacSha256));returnnewJwtSecurityTokenHandler().WriteToken(token);}
在登录方法中,将其写入响应cookie中,如下这般
///<summary>///模拟登录///</summary>///<returns></returns>[HttpPost][AllowAnonymous]publicasyncTask<IActionResult>TestLogin(){varclaims=newClaim[]{newClaim(ClaimTypes.Name,"Jeffcky"),};varclaimsIdentity=newClaimsIdentity(claims,"Login");Response.Cookies.Append("x-access-token",GenerateToken(claims),newCookieOptions(){Path="/",HttpOnly=true});awaitHttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme,newClaimsPrincipal(claimsIdentity));returnOk();}
那么JWT是如何验证Token的呢?默认是从请求去取Bearer Token值,若成功取到这赋值给如下context.Token,所以此时我们需要手动从cookie中取出token并赋值
options.Events=newJwtBearerEvents{OnMessageReceived=context=>{varaccessToken=context.Request.Cookies["x-access-token"];if(!string.IsNullOrEmpty(accessToken)){context.Token=accessToken;}returnTask.CompletedTask;}};
一切已就绪,接下来我们写个api接口测试验证看看
[Authorize("Bearer")][Route("api/[controller]/[action]")][ApiController]publicclassJwtController:ControllerBase{[HttpGet]publicIActionResultTest(){returnOk("testjwt");}}
思考一下,我们通过Postman模拟测试,会返回401吗?结果会是怎样的呢?
问题不大,主要在于该特性参数为声明指定策略,但我们需要指定认证方案即scheme,修改成如下:
如此在与第三方对接时,请求返回token,后续将token置于请求头中即可验证通过,同时上述取cookie中token并手动赋值,对于对接第三方则是多余,不过是为了诸多其他原因而已
[Authorize(AuthenticationSchemes="Bearer,Cookies")]
注意混合认证方案设置存在顺序,后者将覆盖前者即如上设置,此时将走cookie认证
滑动过期思考扩展
若我们实现基于Cookie滑动过期,同时使用signalr进行数据推送,势必存在问题,因为会一直刷新会话,那么将导致会话永不过期问题,从安全层面角度考虑,我们该如何处理呢?
我们知道票据生命周期存储在上下文AuthenticationProperties属性中,所以在配置Cookie选项事件中我们可以进行自定义处理
publicclassCookieAuthenticationEventsExetensions:CookieAuthenticationEvents{privateconststringTicketIssuedTicks=nameof(TicketIssuedTicks);publicoverrideasyncTaskSigningIn(CookieSigningInContextcontext){context.Properties.SetString(TicketIssuedTicks,DateTimeOffset.UtcNow.Ticks.ToString());awaitbase.SigningIn(context);}publicoverrideasyncTaskValidatePrincipal(CookieValidatePrincipalContextcontext){varticketIssuedTicksValue=context.Properties.GetString(TicketIssuedTicks);if(ticketIssuedTicksValueisnull||!long.TryParse(ticketIssuedTicksValue,outvarticketIssuedTicks)){awaitRejectPrincipalAsync(context);return;}varticketIssuedUtc=newDateTimeOffset(ticketIssuedTicks,TimeSpan.FromHours(0));if(DateTimeOffset.UtcNow-ticketIssuedUtc>TimeSpan.FromDays(3)){awaitRejectPrincipalAsync(context);return;}awaitbase.ValidatePrincipal(context);}privatestaticasyncTaskRejectPrincipalAsync(CookieValidatePrincipalContextcontext){context.RejectPrincipal();awaitcontext.HttpContext.SignOutAsync();}}
在添加Cookie服务时,有对应事件选项,使用如下
options.EventsType=typeof(CookieAuthenticationEventsExetensions);
扩展事件实现表示在第一次会话到当前时间截止超过3天,则自动重定向至登录页,最后将上述扩展事件进行注册即可
“.NETCore怎么实现Cookie和JWT混合认证、授权”的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识可以关注亿速云网站,小编将为大家输出更多高质量的实用文章!