本文主要分为两个部分:

1、概念

2、.net 8 demo

第一部分主要描述所有与 JWT 相关的概念以及词汇,及其原理;第二部分是代码示例,文末附 源码下载。

* 阅读提示 :鼠标悬停在 章节标题 上可见 文章目录

1、概念

JWT

json web token 是一种开放标准(RFC 7519),是指定一种数据格式(json) 和数据结构 (header, payload, signature) ,

用于网络应用间信息传输,一般是用于客户端与服务器之间的安全校验

authentication

鉴权;可以为用户创建 token,可以校验 token 有效性

authorization

批准 / 授权;是一种配置,约束访问条件,例如:

[Authorize(Roles = "Admin")] // 管理员才可访问

[Authorize(Policy = "EmployeeWithDepartment")] // 符合特定策略才可访问

bearer

持票人,使用场景在于请求时,header 的格式

例如请求头部的参数 "Authorization" : "Bearer tokenString"

一个 token 由 3 个部分组成,并且以实心句号 . 分割,以下为一个 token 示例:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoidXNlcjEyMyIsIm5iZiI6MTczMTM5MzYzNiwiZXhwIjoxNzMxMzk3MjM2LCJpYXQiOjE3MzEzOTM2MzYsImlzcyI6InlvdXJkb21haW4uY29tIiwiYXVkIjoieW91ci1hcGktYXVkaWVuY2UifQ.q2zUeZ3RAVIxg5CVNI2Pjq9Nfvue3_tCQagDRJmStYI

token part

decode (by base64)

remark

header

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9

{ "alg" : "HS256", "typ" : "JWT" }

token metadata

说明用于加密的算法类型

payload

(有效载荷)

eyJuYW1lIjoidXNlcjEyMyIsIm5iZiI6MTczMTM5MzYzNiwiZXhwIjoxNzMxMzk3MjM2LCJpYXQiOjE3MzEzOTM2MzYsImlzcyI6InlvdXJkb21haW4uY29tIiwiYXVkIjoieW91ci1hcGktYXVkaWVuY2UifQ

{

"name": "user123",

"nbf": 1731393636,

"exp": 1731397236,

"iat": 1731393636,

"iss": "yourdomain.com",

"aud": "your-api-audience"

}

自定义的用户信息(claims),

以及一些必要信息:

nbf : not before, token 生效时间

exp : expiration time, token 过期时间

iat : issued at 签发时间

iss : issuer of the token 签发者(比如服务端名字)

aud : audience,该 token 的受众,可以是服务名称 或者 api 名称等等

signature

q2zUeZ3RAVIxg5CVNI2Pjq9Nfvue3_tCQagDRJmStYI

N/A

signature:是根据 header 中指定的算法以及在服务端密钥,对 payload 加密得到的哈希值;

用于服务端校验 token 是否签发自服务端。

(请求时 token 的 payload 与解密 signature 后得到的payload 需要一致,所以擅改 payload 会导致token 无效)

* base64 transfer online : Base64编码_base64在线解码_base64加密在线转换

什么是无状态化?

无状态 (stateless) :指服务端不需要存储任何关于用户会话的信息。

一般地,在旧时的实现方式中,服务端可能需要存储用户登录的记录,例如需要存储对应用户会话的有效时间。

而 JWT 标准中 signature 的设计允许了服务端可以不对登录信息做持久化,服务端可以通过算法验证 signature 的有效性,以此确保了 token 的安全性,所以并不需要另外存储用户会话状态,这样就减少服务端压力和复杂性,做到“无状态”化。

但其实现今的应用需求中,很多时候都希望追踪到用户行为,所以还是会 log 用户操作,为日后的用户行为分析做数据奠基;当然,这是另一个话题。

2、.net 8 demo

2.1 Authentication

包引用

using Microsoft.IdentityModel.Tokens;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;

创建 token 的方法

 1 public string GenerateJwtToken(string userName)
2 {
3
4 var tokenHander = new JwtSecurityTokenHandler();
5 var key = Encoding.ASCII.GetBytes("your secret key");
6 var tokenDescriptor = new SecurityTokenDescriptor
7 {
8 // configure the custome information in the Claim instance
9 Subject = new System.Security.Claims.ClaimsIdentity(new[] { new Claim("name", userName) }),
10
11 // configure the expiration time
12 Expires = DateTime.UtcNow.AddHours(1),
13
14 // issuer could be your service name or other, and it will be the [iss] of the token
15 Issuer = "xx Servicing",
16
17 // audience could be the audience of your service, and it will be the [aud] of the token
18 Audience = "your api audience",
19
20 // configure secret algorithm and secret key for signature part
21 SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature)
22
23 };
24
25 var token = tokenHander.CreateToken(tokenDescriptor);
26 return tokenHander.WriteToken(token);
27 }

验证 token 的配置

 1 builder.Services.AddAuthentication(options =>
2 {
3 options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
4 options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
5 })
6 .AddJwtBearer(options =>
7 {
8 options.TokenValidationParameters = new TokenValidationParameters
9 {
10 // validate the token expiration
11 ValidateLifetime = true,
12
13 // validate signature
14 ValidateIssuerSigningKey = true,
15
16 // need to sure the issuer/audience value here is same with the issuer/audience while token generating
17 ValidateIssuer = true,
18 ValidateAudience = true,
19 ValidIssuer = "yourdomain.com",
20 ValidAudience = "your-api-audience",
21
22 //ValidateIssuer = false, // Skip issuer validation
23 //ValidateAudience = false, // Skip audience validation
24
25 IssuerSigningKey = new SymmetricSecurityKey("your secret key"),
26 ClockSkew = TimeSpan.FromMinutes(5) // Allow for clock skew
27
28 };
29
30 });

开启 authentication 和 authorization

1 app.UseAuthentication();
2 app.UseAuthorization();

!注意:

1、此处的 Authentication,Authorization 都是 Miscroft.AspNetCore 自带的中间件。

2、Authentication 是鉴权,Authorization 是授权 / 批准,这两个中间件的位置不能错,必须是:鉴权在前。

Authentication 中间件主要负责验证 token ,比如检查签名是否正确,检查 token 有效期。

当 Authentication 中间件成功验证令牌后,它会将 token 中的用户信息(像是用户ID、角色等)写入到 httpContext,再传递给 Authorization 中间件。此时,Authorization 会拿到有效的 httpContext.User 属性,这是一个 ClaimsPrincipal 对象,包含了用户信息声明(claims);

若绕开了 Authentication 中间件,httpContext 直接到 Authorization ,此时会找不到合法的 httpContext.User,所以返回 401 Unauthorized 错误。

源码下载

1、.net 8 AuthDemo Web Api Project

2、AuthDemo.postman_collection.json

2.2 Authorization

2.2.1 Authorize

1     // 标注 Authorize 后访问该api时会验证token
2 [Authorize]
3 [HttpGet("GetList")]
4 public IEnumerable<WeatherForecast> GetList()
5 {
6 ..
7 }

2.2.2 Policy 策略

 1     // 在 Startup.cs 中配置策略
2 services.AddAuthorization(options =>
3 {
4 options.AddPolicy("Over18", policy =>
5 policy.RequireClaim("Age", "18")); // 验证 token 中的 age 参数是否等于 18
6 });
7
8 // 在 Controller 中使用策略
9 [Authorize(Policy = "Over18")]
10 public IActionResult RestrictedContent()
11 {
12 ..
13 }
 1     // 验证 token 中的 role 参数是否 ("Admin", "Manager")中的成员,注:大小写敏感
2 services.AddAuthorization(options =>
3 {
4 options.AddPolicy("AdminOrManager", policy =>
5 policy.RequireRole("Admin", "Manager"));
6 });
7
8 // 多个“并且”条件的声明
9 services.AddAuthorization(options =>
10 {
11
12 options.AddPolicy("EmployeeWithDepartment", policy =>
13 policy.RequireClaim("EmployeeNumber")
14 .RequireClaim("Department", "HR", "IT"));
15 });

2.2.3 AllowAnonymous

[Authorize] 可以修饰在 controller 上,这意味着该 controller 下的所有 api 都需要验证token,

此时可以通过修饰 [AllowAnonymous] 特别指定某一个 api 不需要 token 验证。

 1 [Authorize]
2 public class AccountController : Controller
3 {
4 [AllowAnonymous]
5 public IActionResult Login()
6 {
7 ..
8 }
9
10 public IActionResult Logout()
11 {
12 ..
13 }
14 }

博客园的编辑器也太难用了吧!!

2024-11-13

.net 8 实现 JWT 无状态设计 token [附源码]的更多相关文章

  1. WPF一步步实现完全无边框自定义Window(附源码)

    在我们设计一个软件的时候,有很多时候我们需要按照美工的设计来重新设计整个版面,这当然包括主窗体,因为WPF为我们提供了强大的模板的特性,这就为我们自定义各种空间提供了可能性,这篇博客主要用来介绍如何自 ...

  2. JAVA课程设计(附源码)

    Java课程设计选题 Java课程设计说明 本次课程设计的目的是通过课程设计的各个项目的综合训练,培养学生实际分析问题.编程和动手能力,提高学生的综合素质.本课程设计尝试使用一些较生动的设计项目,激发 ...

  3. 分享基于Entity Framework的Repository模式设计(附源码)

    关于Repository模式,在这篇文章中有介绍,Entity Framework返回IEnumerable还是IQueryable? 这篇文章介绍的是使用Entity Framework实现的Rep ...

  4. python实现抖音多线程下载无水印视频【附源码】

    昨天发了一个无水印解析,评论说想要多线程下载,还是比较简单的. py文件同目录下创建url.txt,把链接一行一行复制进去,就能批量下载. 代码中的延时不能去掉,由于是多线程,速度较快,延时很重要. ...

  5. 【转】.NET(C#):浅谈程序集清单资源和RESX资源 关于单元测试的思考--Asp.Net Core单元测试最佳实践 封装自己的dapper lambda扩展-设计篇 编写自己的dapper lambda扩展-使用篇 正确理解CAP定理 Quartz.NET的使用(附源码) 整理自己的.net工具库 GC的前世与今生 Visual Studio Package 插件开发之自动生

    [转].NET(C#):浅谈程序集清单资源和RESX资源   目录 程序集清单资源 RESX资源文件 使用ResourceReader和ResourceSet解析二进制资源文件 使用ResourceM ...

  6. 【转载】深度解读 java 线程池设计思想及源码实现

    总览 开篇来一些废话.下图是 java 线程池几个相关类的继承结构: 先简单说说这个继承结构,Executor 位于最顶层,也是最简单的,就一个 execute(Runnable runnable) ...

  7. Java并发指南12:深度解读 java 线程池设计思想及源码实现

    ​深度解读 java 线程池设计思想及源码实现 转自 https://javadoop.com/2017/09/05/java-thread-pool/hmsr=toutiao.io&utm_ ...

  8. Django-jwt token生成源码分析

    一. 认证的发展历程简介 这里真的很简单的提一下认证的发展历程.以前大都是采用cookie.session的形式来进行客户端的认证,带来的结果就是在数据库上大量存储session导致数据库压力增大,大 ...

  9. Java开源生鲜电商平台-支付模块的设计与架构(源码可下载)

    Java开源生鲜电商平台-支付模块的设计与架构(源码可下载) 开源生鲜电商平台支付目前支持支付宝与微信.针对的是APP端(android or IOS)   1. 数据库表设计. 说明:无论是支付宝还 ...

  10. Java开源生鲜电商平台-推荐系统模块的设计与架构(源码可下载)

    Java开源生鲜电商平台-推荐系统模块的设计与架构(源码可下载) 业务需求: 对于一个B2B的生鲜电商平台,对于买家而言,他需要更加快速的购买到自己的产品,跟自己的餐饮店不相关的东西,他是不关心的,而 ...

随机推荐

  1. GienTech动态|长沙共建交付中心启动、联合华为举办金融CXO沙龙、亮相大湾区多场科技盛会

    ---- GienTech动态 ---- 长沙共建交付中心启动仪式圆满举办 11月23日,由中电金信和太平洋人寿保险联合举办的长沙共建交付中心启动仪式顺利举行.太平洋人寿保险总经理助理.首席信息官黄鲲 ...

  2. sqlalchemy 的 schema 合并模块 alembic 使用

    alembic 很好的解决了升级数据库改变表结构的传统难题,官方的推荐用法是当一个工具用,这是从 Stack Overflow 扒到的直接用内部 api 的代码,操作有点像 diff_patch. 来 ...

  3. 聊一聊 C#线程池 的线程动态注入 (上)

    一:背景 1. 讲故事 在线程饥饿的场景中,我们首先要了解的就是线程是如何动态注入的?其实现如今的ThreadPool内部的实现逻辑非常复杂,而且随着版本的迭代内部逻辑也在不断的变化,有时候也没必要详 ...

  4. [双体系练习]Java基础练习题1

    因为练习是word,本文我只是写了里面的部分内容,如果想查阅完整内容或者获取word以及PDF,请 关注微信公众号 乖乖狼科技 发送口令 akcd T1 静态代码块中不能? · [D ] A. 初始化 ...

  5. Windows下,terminal美化、命令行美化

    1. Terminal terminal 比 原生的 cmd 要更加好用 直接去 Micorosoft Store 下载就行了 2. 美化效果图 3. 美化步骤 3.1 需要的插件 git-alias ...

  6. 人工生命(AL:Artificial life)兰顿蚂蚁多版本代码html\go\php\python\java

    背景介绍 人工生命(AL:Artificial life)这一概念由美国计算机科学家.人工生命领域创始人之一克里斯托弗・盖尔・兰顿(Christopher G. Langton)提出.1986 年,兰 ...

  7. Qt音视频开发12-mpv解码播放

    一.前言 之前玩了vlc解码和ffmpeg解码,前阵子有个客户需要换成mpv解码,于是研究了下mpv的使用方法,自从用了mpv以后发现爱不释手,这玩意天生适合极客和程序员啊,居然将各种处理封装成了命令 ...

  8. [转]EasyUI 搭建后台登录界面和管理系统主界面

    原文链接: EasyUI 搭建后台登录界面和管理系统主界面

  9. 零基础IM开发入门(四):什么是IM系统的消息时序一致性?

    本文引用了沈剑<如何保证IM实时消息的"时序性"与"一致性"?>一文的图片和内容(由于太懒,图没重新画),原文链接在文末. 1.引言 本文接上篇&l ...

  10. IM跨平台技术学习(二):Electron初体验(快速开始、跨进程通信、打包、踩坑等)

    本文由蘑菇街前端技术团队分享,原题"Electron 从零到一",有修订和改动. 1.引言 在上篇<快速了解新一代跨平台桌面技术--Electron>,我们已经对Ele ...