ASP.NET Core 中jwt授权认证的流程原理
ASP.NET Core 中jwt授权认证的流程原理
目录
1,快速实现授权验证
1.1 添加 JWT 服务配置
1.2 颁发 Token
1.3 添加 API访问
2,探究授权认证中间件
2.1 实现 Token 解析
2.2 实现校验认证
1,快速实现授权验证
什么是 JWT ?为什么要用 JWT ?JWT 的组成?
这些百度可以直接找到,这里不再赘述。
实际上,只需要知道 JWT 认证模式是使用一段 Token 作为认证依据的手段。
我们看一下 Postman 设置 Token 的位置。
那么,如何使用 C# 的 HttpClient 访问一个 JWT 认证的 WebAPI 呢?
下面来创建一个 ASP.NET Core 项目,尝试添加 JWT 验证功能。
1.1 添加 JWT 服务配置
在 Startup.cs 的 ConfigureServices 方法中,添加一个服务
// 设置验证方式为 Bearer Token // 你也可以添加 using Microsoft.AspNetCore.Authentication.JwtBearer; // 使用 JwtBearerDefaults.AuthenticationScheme 代替 字符串 "Brearer" services.AddAuthentication("Bearer") .AddJwtBearer(options => { options.TokenValidationParameters = new TokenValidationParameters { ValidateIssuerSigningKey = true, IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("abcdABCD1234abcdABCD1234")), // 加密解密Token的密钥 // 是否验证发布者 ValidateIssuer = true, // 发布者名称 ValidIssuer = "server", // 是否验证订阅者 // 订阅者名称 ValidateAudience = true, ValidAudience = "client007", // 是否验证令牌有效期 ValidateLifetime = true, // 每次颁发令牌,令牌有效时间 ClockSkew = TimeSpan.FromMinutes(120) }; });
修改 Configure 中的中间件
app.UseHttpsRedirection(); app.UseRouting(); app.UseAuthentication(); // 注意这里 app.UseAuthorization();
就是这么简单,通过以上设置,要求验证请求是否有权限。
1.2 颁发 Token
颁发的 Token ,ASP.NET Core 不会保存。
ASP.NET Core 启用了 Token 认证,你随便将生成 Token 的代码放到不同程序的控制台,只要密钥和 Issuer 和 Audience 一致,生成的 Token 就可以登录这个 ASP.NET Core。
也就是说,可以随意创建控制台程序生成 Token,生成的 Token 完全可以登录 ASP.NET Core 程序。
至于原因,我们后面再说,
在 Program.cs 中,添加一个这样的方法
static void ConsoleToke() { // 定义用户信息 var claims = new Claim[] { new Claim(ClaimTypes.Name, "痴者工良"), new Claim(JwtRegisteredClaimNames.Email, "66666666666@qq.com"), }; // 和 Startup 中的配置一致 SymmetricSecurityKey key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("abcdABCD1234abcdABCD1234")); JwtSecurityToken token = new JwtSecurityToken( issuer: "server", audience: "client007", claims: claims, notBefore: DateTime.Now, expires: DateTime.Now.AddMinutes(30), signingCredentials: new SigningCredentials(key, SecurityAlgorithms.HmacSha256) ); string jwtToken = new JwtSecurityTokenHandler().WriteToken(token); Console.WriteLine(jwtToken); }
Main() 中,调用此方法
public static void Main(string[] args) { ConsoleToke(); CreateHostBuilder(args).Build().Run(); }
1.3 添加 API访问
我们添加一个 API。
[Authorize] 特性用于标识此 Controller 或 Action 需要使用合规的 Token 才能登录。
[Authorize] [Route("api/[controller]")] [ApiController] public class HomeController : ControllerBase { public string Get() { Console.WriteLine(User.Claims.FirstOrDefault(x => x.Type == ClaimTypes.Name)); return "访问成功"; } }
然后启动 ASP.NET Core,在 Postman 测试 访问 https://localhost/api/home。
发现报 401 (无权限)状态码,这是因为请求时不携带令牌,会导致不能访问 API。
从控制台终端复制生成的 Token 码,复制到 Postman 中,再次访问,发现响应状态码为 200,响应成功。
ASP.NET Core 自带 jwt 认证大概就是这样。
那么,ASP.NET Core 内部是如何实现的呢?又有哪些特性哪些坑呢?请往下看~
2,探究授权认证中间件
在上面的操作中,我们在管道配置了两个中间件。
app.UseAuthentication(); app.UseAuthorization();
app.UseAuthentication(); 的作用是通过 ASP.NET Core 中配置的授权认证,读取客户端中的身份标识(Cookie,Token等)并解析出来,存储到 context.User 中。
app.UseAuthorization(); 的作用是判断当前访问 Endpoint (Controller或Action)是否使用了 [Authorize]以及配置角色或策略,然后校验 Cookie 或 Token 是否有效。
使用特性设置相应通过认证才能访问,一般有以下情况。
// 不适用特性,可以直接访问 public class AController : ControllerBase { public string Get() { return "666"; } } /// <summary> /// 整个控制器都需要授权才能访问 /// </summary> [Authorize] public class BController : ControllerBase { public string Get() { return "666"; } } public class CController : ControllerBase { // 只有 Get 需要授权 [Authorize] public string Get() { return "666"; } public string GetB() { return "666"; } } /// <summary> /// 整个控制器都需要授权,但 Get 不需要 /// </summary> [Authorize] public class DController : ControllerBase { [AllowAnonymous] public string Get() { return "666"; } }
2.1 实现 Token 解析
至于 ASP.NET Core 中,app.UseAuthentication(); 和 app.UseAuthorization(); 的源代码各种使用了一个项目来写,代码比较多。要理解这两个中间件的作用,我们不妨来手动实现他们的功能。
解析出的 Token 是一个 ClaimsPrincipal 对象,将此对象给 context.User 赋值,然后在 API 中可以使用 User 实例来获取用户的信息。
在中间件中,使用下面的代码可以获取客户端请求的 Token 解析。
context.RequestServices.GetRequiredService<IAuthenticationService>().AuthenticateAsync(context, JwtBearerDefaults.AuthenticationScheme);
那么,我们如何手工从原生的 Http 请求中,解析出来呢?且看我慢慢来分解步骤。
首先创建一个 TestMiddleware 文件,作为中间件使用。
public class TestMiddleware { private readonly RequestDelegate _next; jwtSecurityTokenHandler = new JwtSecurityTokenHandler(); public TestMiddleware(RequestDelegate next) { _next = next; } public async Task Invoke(HttpContext context) { if (context == null) { throw new ArgumentNullException(nameof(context)); } // 我们写代码的区域
// 我们写代码的区域 await _next(context); } }
2.1.1 从 Http 中获取 Token
下面代码可以中 http 请求中,取得头部的 Token 。
当然,客户端可能没有携带 Token,可能获取结果为 null ,自己加个判断。
贴到代码区域。
string tokenStr = context.Request.Headers["Authorization"].ToString();
Header 的 Authorization 键,是由 Breaer {Token}组成的字符串。
2.1.2 判断是否为有效令牌
拿到 Token 后,还需要判断这个 Token 是否有效。
因为 Authorization 是由 Breaer {Token}组成,所以我们需要去掉前面的 Brear 才能获取 Token。
/// <summary> /// Token是否是符合要求的标准 Json Web 令牌 /// </summary> /// <param name="tokenStr"></param> /// <returns></returns> public bool IsCanReadToken(ref string tokenStr) { if (string.IsNullOrWhiteSpace(tokenStr) || tokenStr.Length < 7) return false; if (!tokenStr.Substring(0, 6).Equals(Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerDefaults.AuthenticationScheme)) return false; tokenStr = tokenStr.Substring(7); bool isCan = jwtSecurityTokenHandler.CanReadToken(tokenStr); return isCan; }
获得 Token 后,通过 JwtSecurityTokenHandler.CanReadToken(tokenStr); 来判断 Token 是否符合协议规范。
将下面判断贴到代码区域。
if (!IsCanReadToken(ref tokenStr)) return ;
2.1.3 解析 Token
下面代码可以将 Header 的 Authorization 内容转为 JwtSecurityToken 对象。
(截取字符串的方式很多种,喜欢哪个就哪个。。。)
/// <summary> /// 从Token解密出JwtSecurityToken,JwtSecurityToken : SecurityToken /// </summary> /// <param name="tokenStr"></param> /// <returns></returns> public JwtSecurityToken GetJwtSecurityToken(string tokenStr) { var jwt = jwtSecurityTokenHandler.ReadJwtToken(tokenStr); return jwt; }
不过这个 GetJwtSecurityToken 不是我们关注的内容,我们是要获取 Claim。
JwtSecurityToken.Claims
将下面代码贴到代码区域
JwtSecurityToken jst = GetJwtSecurityToken(tokenStr); IEnumerable<Claim> claims = jst.Claims;
2.1.4 生成 context.User
context.User 是一个 ClaimsPrincipal 类型,我们通过解析出的 Claim,生成 ClaimsPrincipal。
JwtSecurityToken jst = GetJwtSecurityToken(tokenStr); IEnumerable<Claim> claims = jst.Claims; List<ClaimsIdentity> ci = new List<ClaimsIdentity>() { new ClaimsIdentity(claims) }; context.User = new ClaimsPrincipal(ci);
最终的代码块是这样的
// 我们写代码的区域 string tokenStr = context.Request.Headers["Authorization"].ToString(); string requestUrl = context.Request.Path.Value; if (!IsCanReadToken(ref tokenStr)) return; JwtSecurityToken jst = GetJwtSecurityToken(tokenStr); IEnumerable<Claim> claims = jst.Claims; List<ClaimsIdentity> ci = new List<ClaimsIdentity>() { new ClaimsIdentity(claims) }; context.User = new ClaimsPrincipal(ci); var x = new ClaimsPrincipal(ci); // 我们写代码的区域
2.2 实现校验认证
app.UseAuthentication(); 的大概实现过程已经做出了说明,现在我们来继续实现 app.UseAuthorization(); 中的功能。
继续使用上面的中间件,在原代码块区域添加新的区域。
// 我们写代码的区域 // 我们写的代码块 2
2.2.1 Endpoint
Endpoint 标识了一个 http 请求所访问的路由信息和 Controller 、Action 及其特性等信息。
[Authorize] 特性继承了 IAuthorizeData。[AllowAnonymous] 特性继承了 IAllowAnonymous。
以下代码可以获取所访问的节点信息。
var endpoint = context.GetEndpoint();
那么如何判断所访问的 Controller 和 Action 是否使用了认证相关的特性?
var authorizeData = endpoint?.Metadata.GetOrderedMetadata<IAuthorizeData>() ?? Array.Empty<IAuthorizeData>();
Metadata 是一个 ASP.NET Core 实现的集合对象,GetOrderedMetadata 可以找出需要的特性信息。
这个集合不会区分是 Contrller 还是 Action 的 [Authorize] 特性。
那么判断 是否有 [AllowAnonymous] 特性,可以这样使用。
if (endpoint?.Metadata.GetMetadata<IAllowAnonymous>() != null) { await _next(context); return; }
低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
SQL 开发任务超 50% !滴滴实时计算的演进与优化
摘要:Apache Flink 是一个分布式大数据处理引擎,可对有限数据流和无限数据流进行有状态计算。可部署在各种集群环境,对各种大小的数据规模进行快速计算。滴滴基于 Apache Flink 做了大量的优化,也增加了更多的功能,比如扩展 DDL、内置消息格式解析、扩展 UDX 等,使得 Flink 能够在滴滴的业务场景中发挥更大的作用。本文中,滴滴出行实时计算负责人、高级技术专家梁李印分享了 Apache Flink 在滴滴的应用与实践。主要内容为: 服务化概述 StreamSQL 实践 平台化建设 挑战及规则 一、服务化概述 滴滴大数据服务架构 滴滴目前基于开源的大数据生态构建了一个比较完整的大数据体系,这套体系包括了离线、实时、OLAP、HBase 生态、检索以及消息队列等。滴滴大数据体系在 Flink 的基础之上着力发展 StreamSQL,后面也会对此进行详细介绍。 滴滴流计算发展历程 如下图所示的是滴滴流计算的发展历程。在 2017 年之前,滴滴的流计算基本上采用的都是业务方自建小集群的方式,并且小集群的选型也是多种多样的,包括了 Storm、Jstorm、Spark St...
- 下一篇
03月17日云栖号头条:阿里本地生活宣布“七大商家赋能计划”
云栖号:https://yqh.aliyun.com第一手的上云资讯,不同行业精选的上云企业案例库,基于众多成功案例萃取而成的最佳实践,助力您上云决策! 今日最新云头条快讯: 阿里本地生活宣布“七大商家赋能计划” ,据阿里方面介绍,未来在与多个流量入口打通后,本地生活平台将每天为商家带来超过1亿访问用户,佣金继续保持在低于行业3%-5%的水平;近日,工信部召开推动产业链协同复工复产电视电话会议,会议强调,要把推动产业链协同复工复产作为当前工作的重点,切实解决企业实际困难,分级分类推动解决省区内及跨地区跨部门问题。 一起来看最新的资讯: 阿里本地生活宣布“七大商家赋能计划” 3月16日,阿里本地生活宣布“七大商家赋能计划” 。据阿里方面介绍,未来在与多个流量入口打通后,本地生活平台将每天为商家带来超过1亿访问用户,佣金继续保持在低于行业3%-5%的水平。与此同时,饿了么口碑要为100万商家升级“数智中台”,通过私域小程序获得的订单不收一分钱佣金。此外,还将帮助超过5000家商家开通天猫店等。 工信部:推动大企业牵引中小企业抱团复工复产 3月16日,工信部召开推动产业链协同复工复产电视电话...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- CentOS8安装Docker,最新的服务器搭配容器使用
- Docker安装Oracle12C,快速搭建Oracle学习环境
- CentOS7设置SWAP分区,小内存服务器的救世主
- SpringBoot2全家桶,快速入门学习开发网站教程
- CentOS8编译安装MySQL8.0.19
- Docker使用Oracle官方镜像安装(12C,18C,19C)
- CentOS7编译安装Gcc9.2.0,解决mysql等软件编译问题
- CentOS关闭SELinux安全模块
- CentOS6,7,8上安装Nginx,支持https2.0的开启
- CentOS7编译安装Cmake3.16.3,解决mysql等软件编译问题