前言

为防止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混合认证、授权的更多相关文章

  1. asp.net core 3.1多种身份验证方案,cookie和jwt混合认证授权

    开发了一个公司内部系统,使用asp.net core 3.1.在开发用户认证授权使用的是简单的cookie认证方式,然后开发好了要写几个接口给其它系统调用数据.并且只是几个简单的接口不准备再重新部署一 ...

  2. ASP.NET Core 2.2 : 二十七. JWT与用户授权(细化到Action)

    上一章分享了如何在ASP.NET Core中应用JWT进行用户认证以及Token的刷新,本章继续进行下一步,用户授权.涉及到的例子也以上一章的为基础.(ASP.NET Core 系列目录) 一.概述 ...

  3. .Net Core权限认证基于Cookie的认证&授权.Scheme、Policy扩展

    在身份认证中,如果某个Action需要权限才能访问,最开始的想法就是,哪个Action需要权限才能访问,我们写个特性标注到上面即可,[TypeFilter(typeof(CustomAuthorize ...

  4. asp.net core 实现支持多语言

    asp.net core 实现支持多语言 Intro 最近有一个外国友人通过邮件联系我,想用我的活动室预约,但是还没支持多语言,基本上都是写死的中文,所以最近想支持一下更多语言,于是有了多语言方面的一 ...

  5. Dotnet core使用JWT认证授权最佳实践(二)

    最近,团队的小伙伴们在做项目时,需要用到JWT认证.遂根据自己的经验,整理成了这篇文章,用来帮助理清JWT认证的原理和代码编写操作. 第一部分:Dotnet core使用JWT认证授权最佳实践(一) ...

  6. 检测浏览器是否支持cookie方法

    cookie 摘自: http://www.cnblogs.com/fish-li/archive/2011/07/03/2096903.html Cookie是什么? Cookie 是一小段文本信息 ...

  7. 在ASP.NET Core 中使用Cookie中间件

    在ASP.NET Core 中使用Cookie中间件 ASP.NET Core 提供了Cookie中间件来序列化用户主题到一个加密的Cookie中并且在后来的请求中校验这个Cookie,再现用户并且分 ...

  8. ASP.NET CORE中使用Cookie身份认证

    大家在使用ASP.NET的时候一定都用过FormsAuthentication做登录用户的身份认证,FormsAuthentication的核心就是Cookie,ASP.NET会将用户名存储在Cook ...

  9. 使Asp.net Core同时支持输出Json/Xml

    我们知道Asp.net Core是支持输出为Json格式的.同时也支持输出为xml格式.只要我们正确的配置.并在Request时指定正确的Accept,即可根据不同的Header来输出不同的格式. 前 ...

随机推荐

  1. 注册页面的servlet

    package cn.itcast.travel.web.servlet;import cn.itcast.travel.domain.ResultInfo;import cn.itcast.trav ...

  2. numpy基础教程--对数组进行水平拼接和竖直拼接

    在处理数组的时候经常要用到拼接,numpy中有两个非常实用的函数,可以快捷对数组进行拼接 1.hstack(tup)函数可以接收维度相同的数组,进行水平拼接. 2.vstack(tup)用来竖直拼接 ...

  3. Jenkins视图管理

    目录 一.简介 二.视图维护 创建视图 将项目加入视图中 三.状态图标变绿 四.看板 一.简介 在现在的编程中,公司往往采用的是模块化的编程方式,也就是说将一个项目拆分成许多模块,每个小项目组往往只负 ...

  4. Spring MVC环境搭建和配置

    1. 创建Dynamic web project 2. 修改WEB-INF/web.xml,内容如下: <?xml version="1.0" encoding=" ...

  5. 日历共享(Project)

    <Project2016 企业项目管理实践>张会斌 董方好 编著 为了某一个项目,我们建好了一整套的日历,除了标准日历里加上了所有的假期以外,其他来自火星的水星的金星的土星的木星的BT的不 ...

  6. 爆款预订,2022 年最值得关注的后台框架 —— Fantastic-admin

    前言 如果 2021 年你还没有听说过 Fantastic-admin ,那即将到来的 2022 年可不要再错过了. Fantastic-admin 做为一款开箱即用的 Vue 中后台管理系统框架,距 ...

  7. HMS Core版本发布公告

    新增动作捕捉能力.通过简单拍摄即可获得人体3D骨骼关键点数据,广泛应用于虚拟形象.体育运动和医学分析等场景: 3D物体建模能力iOS版本上线. 查看详情>> 新增道路吸附能力.可根据坐标点 ...

  8. CF793A Oleg and shares 题解

    Content 有 \(n\) 支股票,第 \(i\) 支股票原价为 \(a_i\) 卢布.每秒钟可能会有任意一支股票的价格下降 \(k\) 卢布,以至于降到负数.求所有股票的价格均变得相同所要经过的 ...

  9. java 多线程 线程组ThreadGroup;多线程的异常处理。interrupt批量停止组内线程;线程组异常处理

    1,线程组定义: 线程组存在的意义,首要原因是安全.java默认创建的线程都是属于系统线程组,而同一个线程组的线程是可以相互修改对方的数据的.但如果在不同的线程组中,那么就不能"跨线程组&q ...

  10. JAVA微信公众号网页开发——获取公众号关注的所有用户(微信公众号粉丝)

    package com.weixin.sendmessage; import org.apache.commons.lang.StringUtils; import org.apache.http.H ...