实践剖析.NET Core如何支持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]
public class HomeController : Controller
{
public IActionResult Index()
{
return View();
}
}
当进入首页,未认证默认进入account/login,那么接下来创建该视图
public class AccountController : Controller
{
[AllowAnonymous]
public IActionResult Login()
{
return View();
}
......
}
我们启动程序先看看效果
如上图,自动跳转至登录页,此时我们点击模拟登录按钮,发起请求去模拟登录(发起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();
}
上述无非就是构建身份以及该身份下所具有的身份属性,类似个人身份证唯一标识个人,身份证上各个信息即表示如上声明,同时呢,肯定要调用上下文去登录,在整个会话未过期之前,根据认证方案获取对应处理方式,最后将相关信息进行存储等等,有兴趣的童鞋可以去了解其实现细节哈
当我们请求过后,再次访问首页,将看到生成当前会话信息,同时我们将会话过期设置为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 = 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);
}
在登录方法中,将其写入响应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吗?结果会是怎样的呢?
问题不大,主要在于该特性参数为声明指定策略,但我们需要指定认证方案即scheme,修改成如下:
如此在与第三方对接时,请求返回token,后续将token置于请求头中即可验证通过,同时上述取cookie中token并手动赋值,对于对接第三方则是多余,不过是为了诸多其他原因而已
[Authorize(AuthenticationSchemes = "Bearer,Cookies")]
注意混合认证方案设置存在顺序,后者将覆盖前者即如上设置,此时将走cookie认证
滑动过期思考扩展
若我们实现基于Cookie滑动过期,同时使用signalr进行数据推送,势必存在问题,因为会一直刷新会话,那么将导致会话永不过期问题,从安全层面角度考虑,我们该如何处理呢?
我们知道票据生命周期存储在上下文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服务时,有对应事件选项,使用如下
options.EventsType = typeof(CookieAuthenticationEventsExetensions);
扩展事件实现表示在第一次会话到当前时间截止超过3天,则自动重定向至登录页,最后将上述扩展事件进行注册即可
总结
暂无,下次再会!
实践剖析.NET Core如何支持Cookie和JWT混合认证、授权的更多相关文章
- asp.net core 3.1多种身份验证方案,cookie和jwt混合认证授权
开发了一个公司内部系统,使用asp.net core 3.1.在开发用户认证授权使用的是简单的cookie认证方式,然后开发好了要写几个接口给其它系统调用数据.并且只是几个简单的接口不准备再重新部署一 ...
- ASP.NET Core 2.2 : 二十七. JWT与用户授权(细化到Action)
上一章分享了如何在ASP.NET Core中应用JWT进行用户认证以及Token的刷新,本章继续进行下一步,用户授权.涉及到的例子也以上一章的为基础.(ASP.NET Core 系列目录) 一.概述 ...
- .Net Core权限认证基于Cookie的认证&授权.Scheme、Policy扩展
在身份认证中,如果某个Action需要权限才能访问,最开始的想法就是,哪个Action需要权限才能访问,我们写个特性标注到上面即可,[TypeFilter(typeof(CustomAuthorize ...
- asp.net core 实现支持多语言
asp.net core 实现支持多语言 Intro 最近有一个外国友人通过邮件联系我,想用我的活动室预约,但是还没支持多语言,基本上都是写死的中文,所以最近想支持一下更多语言,于是有了多语言方面的一 ...
- Dotnet core使用JWT认证授权最佳实践(二)
最近,团队的小伙伴们在做项目时,需要用到JWT认证.遂根据自己的经验,整理成了这篇文章,用来帮助理清JWT认证的原理和代码编写操作. 第一部分:Dotnet core使用JWT认证授权最佳实践(一) ...
- 检测浏览器是否支持cookie方法
cookie 摘自: http://www.cnblogs.com/fish-li/archive/2011/07/03/2096903.html Cookie是什么? Cookie 是一小段文本信息 ...
- 在ASP.NET Core 中使用Cookie中间件
在ASP.NET Core 中使用Cookie中间件 ASP.NET Core 提供了Cookie中间件来序列化用户主题到一个加密的Cookie中并且在后来的请求中校验这个Cookie,再现用户并且分 ...
- ASP.NET CORE中使用Cookie身份认证
大家在使用ASP.NET的时候一定都用过FormsAuthentication做登录用户的身份认证,FormsAuthentication的核心就是Cookie,ASP.NET会将用户名存储在Cook ...
- 使Asp.net Core同时支持输出Json/Xml
我们知道Asp.net Core是支持输出为Json格式的.同时也支持输出为xml格式.只要我们正确的配置.并在Request时指定正确的Accept,即可根据不同的Header来输出不同的格式. 前 ...
随机推荐
- Mysql的表级锁
我们首先需要知道的一个大前提是:mysql的锁是由具体的存储引擎实现的.所以像Mysql的默认引擎MyISAM和第三方插件引擎 InnoDB的锁实现机制是有区别的.可根据不同的场景选用不同的锁定机制. ...
- Linux系统信息查看命令(ZZ)
http://hi.baidu.com/thinkdifferent/blog/item/22f4a80161630e011d958384.html转自一个baidu师兄的博客,很好的一个总结,推荐下 ...
- 连接 MySQL 数据库出现问题:The server time zone value ‘�й���ʱ��‘ is unrecogni....
出现问题 The server time zone value '�й���ʱ��' is unrecogni.... 解决方案 在 URL 后面加上 ?serverTimezone=UTC 如下: ...
- 2.7 Rust Structs
A struct, or structure, is a custom data type that lets you name and package together multiple relat ...
- TSN(时间敏感网络)测试、仿真、分析平台应用攻略
前言 在汽车领域,近几年车内网络通讯方式的变革诉求,期望能够有更高的数据传输速率,以及保证实时性的通讯方式引入.例如对于ADAS而言,传统的CAN总线已经远远不能满足其对通讯的要求,而基于车载以太网的 ...
- linux小应用 —— 日志过滤
先说问题,统计一个日志文件中去重之后的ip地址的个数.其实这是一个非常常见也比较简单的问题,其中我个人认为最主要的应该是匹配ip地址是这个问题的核心.剩下的就是对linux命令的熟练程度的问题了.首先 ...
- java 多线程:线程通信-等待通知机制wait和notify方法;(同步代码块synchronized和while循环相互嵌套的差异);管道通信:PipedInputStream;PipedOutputStream;PipedWriter; PipedReader
1.等待通知机制: 等待通知机制的原理和厨师与服务员的关系很相似: 1,厨师做完一道菜的时间不确定,所以厨师将菜品放到"菜品传递台"上的时间不确定 2,服务员什么时候可以取到菜,必 ...
- python爬取信息到数据库与mysql简单的表操作
python 爬取豆瓣top250并导入到mysql数据库中 import pymysql import requests import re url='https://movie.douban.co ...
- IDEA报错 Error:(24, 35) java: 常量字符串过长
修改设置
- 使用JS对字符串进行MD5加密
md5.js /* * A JavaScript implementation of the RSA Data Security, Inc. MD5 Message * Digest Algorith ...