详细分析Spring Security OAuth2中的JwtAccessTokenConverter基本知识(附Demo)

目录

  • 前言
  • 1. 基本知识
  • 2. Demo
  • 3. 实战

前言

  1. java框架 零基础从入门到精通的学习路线 附开源项目面经等(超全)
  2. 【Java项目】实战CRUD的功能整理(持续更新)

1. 基本知识

JwtAccessTokenConverter 是 Spring Security OAuth2 中的一个重要组件,主要用于生成和验证 JSON Web Token (JWT)

  1. JWT 的结构
    JWT 由三部分组成:头部(Header)、负载(Payload)和签名(Signature)
    头部通常包含令牌的类型(JWT)和签名算法(如 HMAC SHA256)
    负载部分包含声明(Claims),即与用户相关的信息,例如权限、过期时间等
    签名是使用头部和负载生成的,用于验证令牌的完整性

  2. 类继承关系
    JwtAccessTokenConverter 继承自 AbstractTokenConverter,并实现了 AccessTokenConverter 接口
    该类负责将 OAuth2 访问令牌与 JWT 进行转换和处理

  3. 主要方法
    encode:用于生成 JWT,接受 OAuth2AccessToken 和 OAuth2Authentication 参数
    decode:用于解析和验证 JWT,返回 OAuth2AccessToken 实例

常用的几个类如下:

  • Access Token
    OAuth2 中的访问令牌,用于授权客户端访问受保护资源
    JWT 是一种常用的访问令牌格式

  • OAuth2Authentication
    封装与用户相关的身份验证信息,包括用户的身份和权限

  • Additional Information
    JWT 中可以包含附加信息,通常用于存储用户 ID、用户名、权限等自定义字段

2. Demo

  1. 签名密钥:在 main 方法中使用 demo.setSigningKey(signingKey); 设置签名密钥,以确保生成的 JWT 是安全的

  2. 用户权限和身份验证:使用 SimpleGrantedAuthority 创建用户的角色,并通过 TestingAuthenticationToken 实现身份验证

  3. OAuth2AccessToken:创建访问令牌,附加用户 ID、用户名和权限信息

  4. JWT 生成与解码:调用 encode 方法生成 JWT,然后使用 Base64 解码打印出 JWT 的头部和负载部分

常用的xml配置文件如下:

<!-- https://mvnrepository.com/artifact/org.springframework.security.oauth/spring-security-oauth2 -->
<dependency><groupId>org.springframework.security.oauth</groupId><artifactId>spring-security-oauth2</artifactId><version>2.5.0.RELEASE</version>
</dependency>
<dependency><groupId>org.springframework.security</groupId><artifactId>spring-security-jwt</artifactId><version>1.1.0.RELEASE</version> <!-- 确保版本与 Spring Security 兼容 -->
</dependency>

执行代码如下:

package JwtDemo;import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.common.exceptions.InvalidTokenException;
import org.springframework.security.oauth2.provider.OAuth2Request;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;import java.util.*;public class JwtDemo extends JwtAccessTokenConverter {public static void main(String[] args) {JwtDemo demo = new JwtDemo();String signingKey = "your-signing-key"; // 请替换为你的签名密钥demo.setSigningKey(signingKey);// 创建用户权限Set<SimpleGrantedAuthority> authorities = new HashSet<>();authorities.add(new SimpleGrantedAuthority("ROLE_USER"));// 创建 OAuth2RequestOAuth2Request request = new OAuth2Request(null, "client_id",authorities, true, null, null, null, null, null);// 创建 AuthenticationAuthentication authentication = new TestingAuthenticationToken("user", "password", authorities);// 创建 OAuth2AuthenticationOAuth2Authentication oauth2Authentication = new OAuth2Authentication(request, authentication);// 创建 OAuth2AccessToken 实例DefaultOAuth2AccessToken token = new DefaultOAuth2AccessToken("your-jwt-token");Map<String, Object> additionalInformation = new HashMap<>();additionalInformation.put("user_id", "12345"); // 添加用户信息additionalInformation.put("user_name", "user"); // 添加用户名additionalInformation.put("authorities", authorities); // 添加权限token.setAdditionalInformation(additionalInformation);// 使用 JwtDemo 生成 JWTString jwt = demo.encode(token, oauth2Authentication);System.out.println("生成的 JWT: " + jwt);String[] chunks = jwt.split("\\.");Base64.Decoder decoder = Base64.getUrlDecoder();try {String header = new String(decoder.decode(chunks[0]));String payload = new String(decoder.decode(chunks[1]));System.out.println("Header: " + header);System.out.println("Payload: " + payload);} catch (IllegalArgumentException e) {System.err.println("解码时出错: " + e.getMessage());}}
}// 自定义的 Authentication 实现
class TestingAuthenticationToken extends org.springframework.security.authentication.UsernamePasswordAuthenticationToken {public TestingAuthenticationToken(Object principal, Object credentials, Collection<? extends GrantedAuthority> authorities) {super(principal, credentials, authorities);}
}

截图如下:

在这里插入图片描述

3. 实战

已Bledex源码实战为例

@Configuration  // 标记为配置类,Spring会自动扫描并加载
@ConditionalOnProperty(prefix = "blade.security.oauth2", name = "storeType", havingValue = "jwt", matchIfMissing = true) 
// 当配置中 "blade.security.oauth2.storeType" 为 "jwt" 时,才加载此配置类
public class JwtTokenStoreConfiguration {/*** 使用 jwtTokenStore 存储 token*/@Bean  // 将方法的返回值注册为 Spring 的 Beanpublic TokenStore jwtTokenStore(JwtProperties jwtProperties) {// 创建 JwtTokenStore 实例,并传入 JwtAccessTokenConverterreturn new JwtTokenStore(jwtAccessTokenConverter(jwtProperties));}/*** 用于生成 jwt*/@Bean  // 将方法的返回值注册为 Spring 的 Beanpublic JwtAccessTokenConverter jwtAccessTokenConverter(JwtProperties jwtProperties) {JwtAccessTokenConverter accessTokenConverter = new JwtAccessTokenConverter();// 设置 JWT 签名密钥accessTokenConverter.setSigningKey(jwtProperties.getSignKey());return accessTokenConverter;  // 返回配置好的 JwtAccessTokenConverter}/*** 用于扩展 jwt*/@Bean@ConditionalOnMissingBean(name = "jwtTokenEnhancer") // 当 Spring 容器中没有名为 "jwtTokenEnhancer" 的 Bean 时,才加载此 Beanpublic TokenEnhancer jwtTokenEnhancer(JwtAccessTokenConverter jwtAccessTokenConverter, JwtProperties jwtProperties) {// 创建并返回自定义的 TokenEnhancerreturn new BladeJwtTokenEnhancer(jwtAccessTokenConverter, jwtProperties);}}

对应的自定义类如下:

@AllArgsConstructor  // 自动生成一个包含所有字段的构造函数
public class BladeJwtTokenEnhancer implements TokenEnhancer {private final JwtAccessTokenConverter jwtAccessTokenConverter;  // JWT 访问令牌转换器private final JwtProperties jwtProperties;  // JWT 配置属性@Overridepublic OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {// 从身份验证对象中获取用户的详细信息BladeUserDetails principal = (BladeUserDetails) authentication.getUserAuthentication().getPrincipal();// token 参数增强Map<String, Object> info = new HashMap<>(16);  // 创建一个新的 HashMap 用于存储附加信息info.put(TokenUtil.CLIENT_ID, TokenUtil.getClientIdFromHeader());  // 获取客户端 IDinfo.put(TokenUtil.USER_ID, Func.toStr(principal.getUserId()));  // 获取用户 IDinfo.put(TokenUtil.DEPT_ID, Func.toStr(principal.getDeptId()));  // 获取部门 IDinfo.put(TokenUtil.POST_ID, Func.toStr(principal.getPostId()));  // 获取职位 IDinfo.put(TokenUtil.ROLE_ID, Func.toStr(principal.getRoleId()));  // 获取角色 IDinfo.put(TokenUtil.TENANT_ID, principal.getTenantId());  // 获取租户 IDinfo.put(TokenUtil.OAUTH_ID, principal.getOauthId());  // 获取 OAuth IDinfo.put(TokenUtil.ACCOUNT, principal.getAccount());  // 获取账户信息info.put(TokenUtil.USER_NAME, principal.getUsername());  // 获取用户名info.put(TokenUtil.NICK_NAME, principal.getName());  // 获取昵称info.put(TokenUtil.REAL_NAME, principal.getRealName());  // 获取真实姓名info.put(TokenUtil.ROLE_NAME, principal.getRoleName());  // 获取角色名称info.put(TokenUtil.AVATAR, principal.getAvatar());  // 获取用户头像info.put(TokenUtil.LICENSE, TokenUtil.LICENSE_NAME);  // 获取许可证名称((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(info);  // 将附加信息设置到访问令牌中// token 状态设置if (jwtProperties.getState()) {// 如果 JWT 状态为 true,则增强访问令牌OAuth2AccessToken oAuth2AccessToken = jwtAccessTokenConverter.enhance(accessToken, authentication);String tokenValue = oAuth2AccessToken.getValue();  // 获取增强后的 token 值String tenantId = principal.getTenantId();  // 获取租户 IDString userId = Func.toStr(principal.getUserId());  // 获取用户 ID// 将访问令牌存储到 JwtUtil 中,以便后续管理JwtUtil.addAccessToken(tenantId, userId, tokenValue, accessToken.getExpiresIn());}return accessToken;  // 返回增强后的访问令牌}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/440270.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

一、Python(介绍、环境搭建)

一、介绍 Python 是一种高级编程语言&#xff0c;具有简洁易读的语法、丰富的库和强大的功能。Python是解释型语言&#xff0c;运行代码必须依赖安装好的解释器。Python目前存在两个版本&#xff1a;Python2、Python3&#xff08;主流使用&#xff09; 二、环境搭建 1.安装P…

<<迷雾>> 第8章 学生时代的走马灯(3)--走马灯 示例电路

几个首尾相连的触发器使用同一个控制端&#xff0c;能同时触发 info::操作说明 鼠标单击开关切换开合状态 注: 其中 CP 为按钮开关, 每点击一次, Q 的输出前进一级 注: 第一个触发器的输出端 Q 需要先置入高电平. 如果重置了电路, 可外接电源先使第一个 Q 置入高电平. 另: 因为…

深度学习:5种经典神经网络模型介绍

目录 1. LeNet&#xff1a;CNN的鼻祖 2. AlexNet&#xff1a;深度学习的开山之作 3. VGGNet&#xff1a;深度与简洁的结合 4. GoogLeNet&#xff1a;Inception模块的创新 5. ResNet&#xff1a;残差学习的革命 卷积神经网络&#xff08;CNN&#xff09;已经发展为图像识别…

棋牌灯控计时计费系统软件免费试用版怎么下载 佳易王计时收银管理系统操作教程

一、前言 【试用版软件下载&#xff0c;可以点击本文章最下方官网卡片】 棋牌灯控计时计费系统软件免费试用版怎么下载 佳易王计时收银管理系统操作教程 棋牌计时计费软件的应用也提升了顾客的服务体验&#xff0c;顾客可以清晰的看到自己的消费时间和费用。增加了消费的透明…

梯度下降学习

前言&#xff1a;初步学习梯度下降&#xff0c; 不断根据梯度修改我们的参数&#xff0c;经过多次轮次得到使得我们损失函数最小的时候参数&#xff0c;后续我们可以通过类似的道理&#xff0c;更新我们的参数 假设我们的损失函数是 y x 1 2 x 2 2 y x1^2 x2^2 yx12x22,我…

用Python实现运筹学——Day 14: 线性规划总结与案例复习

一、学习内容 在本节中&#xff0c;我们将复习之前所学的线性规划模型与求解方法&#xff0c;并通过一个综合案例将这些知识应用于求解一个多阶段的生产计划问题。 主要复习内容包括&#xff1a; 线性规划的基础概念&#xff1a;目标函数、约束条件、决策变量。求解方法&…

什么是 HTTP 请求中的 preflight 类型请求

在浏览器的 HTTP 请求中&#xff0c;当我们使用 fetch API 或者 XMLHttpRequest 来进行跨域请求时&#xff0c;浏览器有时会发送一种称为 Preflight 的请求。这种请求是浏览器在实际发送跨域请求前&#xff0c;先与目标服务器进行的一次 “探测” 请求&#xff0c;以确认服务器…

组合式API

1.入口&#xff1a;setup setup中的数据和方法必须return出去&#xff0c;模板才能使用 <script> export default {setup () {console.log(setup);const message this is a messageconst logMessage () > {console.log(message);}return {message,logMessage}},be…

Visual Studio 2017编译libexpat源码过程

一、编译环境 操作系统&#xff1a;Windows 10 企业版 64位 编译工具&#xff1a;Visual Studio 2017 构建工具&#xff1a;CMake3.22 源码版本&#xff1a;libexpat-R_2_4_0 二、CMake生成解决方案 解压libexpat源码&#xff0c;然后启动CMake选择libexpat源码目录&#xff1…

数据结构 ——— 单链表oj题:链表的回文结构

目录 题目要求 手搓简易单链表 代码实现 题目要求 对于一个单链表&#xff0c;设计一个时间复杂度为O(N)&#xff0c;空间复杂度为O(1)的算法&#xff0c;判断其是否为回文结构&#xff0c;给定一个链表的头指针 head&#xff0c;返回一个 bool 值&#xff0c;代表其是否为…

从认识String类,到走进String类的世界

作为一个常用的数据类型&#xff0c;跟随小编一同进入String的学习吧&#xff0c;领略String的一些用法。 1. 认识 String 类 2. 了解 String 类的基本用法 3. 熟练掌握 String 类的常见操作 4. 认识字符串常量池 5. 认识 StringBuffer 和 StringBuilder 一&#xff1a;…

Selenium WebDriver和Chrome对照表

PS&#xff1a;我的没下载WebDriver 也没配置环境变量 也能用Selenium 网上有说把WebDriver放到chrome的安装目录并将路径配到path中【可能之前用playwright下载过】 查看浏览器版本号 在浏览器的地址栏&#xff0c;输入chrome://version/&#xff0c;回车后即可查看到对应版…

文心一言 VS 讯飞星火 VS chatgpt (363)-- 算法导论24.3 5题

五、Newman 教授觉得自己发现了 Dijkstra 算法的一个更简单的证明。他声称 Dikstra 算法对最短路径上面的每条边的松弛次序与该条边在该条最短路径中的次序相同&#xff0c;因此&#xff0c;路径松弛性质适用于从源结点可以到达的所有结点。请构造一个有向图来说明 Dijkstra 算…

SpringBoot基础(四):bean的多种加载方式

SpringBoot基础系列文章 SpringBoot基础(一)&#xff1a;快速入门 SpringBoot基础(二)&#xff1a;配置文件详解 SpringBoot基础(三)&#xff1a;Logback日志 SpringBoot基础(四)&#xff1a;bean的多种加载方式 目录 一、xml配置文件二、注解定义bean1、使用AnnotationCon…

逻辑回归(下): Sigmoid 函数的发展历史

背景 闲来无事翻了一下之前买的一个机器学习课程及之前记录的网络笔记&#xff0c;发现遇到公式都是截图&#xff0c;甚至是在纸上用笔推导的。重新整理一遍之前逻辑回归函数的学习笔记&#xff0c;主要是为了玩一下 LaTex 语法&#xff0c;写公式挺有意思的。 整理之前三篇笔…

鸿蒙harmonyos next flutter通信之MethodChannel获取设备信息

本文将通过MethodChannel获取设备信息&#xff0c;以此来演练MethodChannel用法。 建立channel flutter代码&#xff1a; MethodChannel methodChannel MethodChannel("com.xmg.test"); ohos代码&#xff1a; private channel: MethodChannel | null nullthis.c…

使用JavaScript写一个网页端的四则运算器

目录 style(内联样式表部分) body部分 html script 总的代码 网页演示 style(内联样式表部分) <style>body {font-family: Arial, sans-serif;display: flex;justify-content: center;align-items: center;height: 100vh;background-color: #f0f0f0;}.calculator {…

Pikachu-目录遍历

目录遍历&#xff0c;跟不安全文件上传下载有差不多&#xff1b; 访问 jarheads.php 、truman.php 都是通过 get 请求&#xff0c;往title 参数传参&#xff1b; 在后台&#xff0c;可以看到 jarheads.php 、truman.php所在目录&#xff1a; /var/www/html/vul/dir/soup 图片…

golang gin入门

gin是个小而精的web开发框架 官方文档 安装 go get -u github.com/gin-gonic/gin最简单的起手代码 package mainimport ("net/http""github.com/gin-gonic/gin" )func main() {r : gin.Default()r.GET("/ping", func(c *gin.Context) {c.JSON…

openpnp - 图像传送方向要在高级校正之前设置好

文章目录 openpnp - 图像传送方向要在高级校正之前设置好笔记图像传送方向的确定END openpnp - 图像传送方向要在高级校正之前设置好 笔记 图像传送方向和JOG面板的移动控制和实际设备的顶部摄像头/底部摄像头要一致&#xff0c;这样才能和贴板子时的实际操作方向对应起来。 …