【springsecurity oauth2授权中心】jwt令牌更换成自省令牌 OpaqueToken P4
前言
前面实现了授权中心授权,客户端拿到access_token后就能请求资源服务器接口
权限的校验都是在资源服务器上进行的,授权服务器颁发的access_token有限期是2小时,也就是说在2小时之内,不管授权服务器那边用户的权限如何变更都不会影响到资源服务器上的资源请求。
这篇来解决这个问题,让客户端对资源服务器的每次请求都要去问问授权服务器access_token是否还有效,有效的话资源服务器才能通过
授权服务器
在 application.yml 里的 grant-types 后面添加一个 client_credentials
# 之前
oauth2:client:#...grant-types: authorization_code,refresh_token# 修改后
oauth2:client:#...grant-types: authorization_code,refresh_token,client_credentials
配置类 AuthorizationServerConfig 里将 OAuth2TokenCustomizer 由 JwtEncodingContext 改成 OAuth2TokenClaimsContext
//之前// @Bean
// public OAuth2TokenCustomizer<JwtEncodingContext> jwtCustomizer() {
// return context -> {
// if (context.getTokenType().getValue().equals("access_token")) {
// Collection<? extends GrantedAuthority> authorities = context.getPrincipal().getAuthorities();
// List<String> authorityNames = authorities.stream()
// .map(GrantedAuthority::getAuthority)
// .collect(Collectors.toList());
// context.getClaims().claim("authorities", authorityNames);
// }
// };
// }//修改后
@Beanpublic OAuth2TokenCustomizer<OAuth2TokenClaimsContext> tokenCustomizer() {return context -> {if (context.getTokenType().getValue().equals("access_token")) {// 从用户详情中提取权限Collection<? extends GrantedAuthority> authorities =context.getPrincipal().getAuthorities();// 将权限列表放入令牌声明context.getClaims().claim("authorities",authorities.stream().map(GrantedAuthority::getAuthority).collect(Collectors.toList()));}};}
资源服务器
添加一个依赖
<dependency><groupId>com.nimbusds</groupId><artifactId>oauth2-oidc-sdk</artifactId><version>11.23.1</version><scope>runtime</scope></dependency>
创建一个自省令牌解析器 CustomAuthoritiesOpaqueTokenIntrospector
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.oauth2.core.DefaultOAuth2AuthenticatedPrincipal;
import org.springframework.security.oauth2.core.OAuth2AuthenticatedPrincipal;
import org.springframework.security.oauth2.server.resource.introspection.OpaqueTokenIntrospector;import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;public class CustomAuthoritiesOpaqueTokenIntrospector implements OpaqueTokenIntrospector {private final OpaqueTokenIntrospector delegate;public CustomAuthoritiesOpaqueTokenIntrospector(OpaqueTokenIntrospector delegate) {this.delegate = delegate;}@Overridepublic OAuth2AuthenticatedPrincipal introspect(String token) {OAuth2AuthenticatedPrincipal principal = this.delegate.introspect(token);Map<String, Object> claims = principal.getAttributes();List<String> authorities = (List<String>) claims.get("authorities");if (authorities == null) throw new SecurityException("没有权限");Collection<GrantedAuthority> grantedAuthorities = authorities.stream().map(auth -> new SimpleGrantedAuthority(auth)) // 直接使用权限名,不加 SCOPE_ 前缀.collect(Collectors.toList());return new DefaultOAuth2AuthenticatedPrincipal(claims, grantedAuthorities);}
}
修改 ResourceServerConfig 配置类
@Beanpublic SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {http.authorizeHttpRequests(authorize -> authorize.anyRequest().authenticated()).oauth2ResourceServer(oauth2 -> oauth2// 注释掉jwt
// .jwt(jwt -> jwt
// .jwkSetUri("http://localhost:9000/oauth2/jwks")
// .jwtAuthenticationConverter(jwtAuthenticationConverter()) // 使用自定义转换器
// )// 添加上 opaque.opaqueToken(opaque -> opaque
// .introspectionUri("http://localhost:9000/oauth2/introspect")
// .introspectionClientCredentials("client", "secret").introspector(customOpaqueTokenIntrospector())));return http.build();}// jwt权限转换器,这里用不到了注释掉
// @Bean
// public JwtAuthenticationConverter jwtAuthenticationConverter() {
// JwtGrantedAuthoritiesConverter grantedAuthoritiesConverter = new JwtGrantedAuthoritiesConverter();
// grantedAuthoritiesConverter.setAuthoritiesClaimName("authorities"); // 指定JWT中权限字段名
// grantedAuthoritiesConverter.setAuthorityPrefix(""); // 去掉默认的"SCOPE_"前缀
//
// JwtAuthenticationConverter jwtAuthenticationConverter = new JwtAuthenticationConverter();
// jwtAuthenticationConverter.setJwtGrantedAuthoritiesConverter(grantedAuthoritiesConverter);
// return jwtAuthenticationConverter;
// }
配置授权服务器的请求 /oauth2/introspect 是内置的,不要修改
@Beanpublic OpaqueTokenIntrospector customOpaqueTokenIntrospector() {// 1. 创建默认的 Introspector(连接授权服务器)OpaqueTokenIntrospector delegate = new NimbusOpaqueTokenIntrospector("http://localhost:9000/oauth2/introspect","client", // client_id"secret" // client_secret 与授权服务器保持一致);// 2. 包装成自定义实现return new CustomAuthoritiesOpaqueTokenIntrospector(delegate);}
测试
拿code换access_token的流程跟前面是一样的,下面用access_token请求一下资源服务器的接口看看
/api/hello 接口因为有权限请求成功
请求 /api/hello 接口时,资源服务器日志
授权服务器日志
总结
- jwt方式简单直接,对授权服务器压力比较小,一次授权能用很长时间,期间授权服务器处于闲置状态。适合内部系统且用户权限不经常变动的系统
- 自省令牌稍微麻烦点,对授权服务器压力大,每次请求都需要授权服务器校验,适合用户权限经常变动的系统。
相关文章:

【springsecurity oauth2授权中心】jwt令牌更换成自省令牌 OpaqueToken P4
前言 前面实现了授权中心授权,客户端拿到access_token后就能请求资源服务器接口 权限的校验都是在资源服务器上进行的,授权服务器颁发的access_token有限期是2小时,也就是说在2小时之内,不管授权服务器那边用户的权限如何变更都…...

诱骗协议芯片支持PD2.0/3.0/3.1/PPS协议,支持使用一个Type-C与电脑传输数据和快充取电功能
快充是由充电器端的充电协议和设备端的取电协议进行握手通讯进行协议识别来完成的,当充电器端的充电协议和设备端的取电协议握手成功后,设备会向充电器发送电压请求,充电器会根据设备的需求发送合适的电压给设备快速供电。 设备如何选择快充…...

变量在template里不好使,在setup好使?
问题: 自定义的一个函数 ,import导入后 setup里面使用正常 ,在template里面说未定义 作用域问题 在 Vue 的模板语法中,模板(template )里能直接访问的是组件实例上暴露的属性和方法。从代码看,…...

OpenCV 图形API(53)颜色空间转换-----将 RGB 图像转换为灰度图像函数RGB2Gray()
操作系统:ubuntu22.04 OpenCV版本:OpenCV4.9 IDE:Visual Studio Code 编程语言:C11 算法描述 将图像从 RGB 色彩空间转换为灰度。 R、G 和 B 通道值的常规范围是 0 到 255。生成的灰度值计算方式如下: dst ( I ) 0.299 ∗ src…...

Trae+DeepSeek学习Python开发MVC框架程序笔记(四):使用sqlite存储查询并验证用户名和密码
继续通过Trae向DeepSeek发问并修改程序,实现程序运行时生成数据库,用户在系统登录页面输入用户名和密码后,控制器通过模型查询用户数据库表来验证用户名和密码,验证通过后显示登录成功页面,验证失败则显示登录失败页面…...

超详细mac上用nvm安装node环境,配置npm
一、安装NVM 打开终端,运行以下命令来安装NVM: curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.5/install.sh | bash 然后就会出现如下代码: > Profile not found. Tried ~/.bashrc, ~/.bash_profile, ~/.zprofile, ~/.…...
STM32 串口通信
引言 在嵌入式系统开发中,串口通信是最基础且重要的通信方式之一。无论是设备调试、模块对接还是远程通信,串口都扮演着关键角色。本文将从通信协议原理出发,结合STM32F4系列MCU,深入讲解串口通信的硬件实现和软件配置࿰…...

hi3516cv610构建音频sample工程代码步骤
hi3516cv610构建音频sample工程代码步骤 sdk版本:Hi3516CV610_SDK_V1.0.1.0 硬件:非es8388 工程代码: 通过网盘分享的文件:audio_easy.zip 链接: https://pan.baidu.com/s/1gx61S_F3-pf6hPyfbGaRXg 提取码: 4gbg --来自百度网盘…...

12.QT-Combo Box|Spin Box|模拟点餐|从文件中加载选项|调整点餐份数(C++)
Combo Box QComboBox 表⽰下拉框 核⼼属性 属性说明currentText当前选中的⽂本currentIndex当前选中的条⽬下标.从0开始计算.如果当前没有条⽬被选中,值为-1editable是否允许修改设为true时, QComboBox 的⾏为就⾮常接近 QLineEdit ,也可以 设置 validatoriconSize下拉框图标…...

UML 顺序图:电子图书馆管理系统的交互之道
目录 一、初识 UML 顺序图 二、电子图书馆管理系统顺序图解析 (一)借阅流程 (二)归还流程 三、顺序图绘画 四、顺序图的优势与价值 五、总结 UML 顺序图是描绘系统组件交互的有力工具。顺序图直观展示消息传递顺序与对象协…...
分布式架构设计与应用:从理论到实践
在云计算、大数据与高并发场景的驱动下,分布式架构已成为现代软件系统的核心技术。它通过将计算、存储与业务逻辑分散到多台机器上,解决了单体架构的扩展性瓶颈与单点故障问题。本文将从设计原则、核心组件到典型应用场景,深入剖析分布式架构…...
Uniapp:view容器(容器布局)
目录 一、基本概述二、属性说明三、常用布局3.1 横向布局3.2 纵向布局3.3 更多布局3.3.1 纵向布局-自动宽度3.3.2 纵向布局-固定宽度3.3.3 横向布局-自动宽度3.3.4 横向布局-居中3.3.5 横向布局-居右3.3.6 横向布局-平均分布3.3.7 横向布局-两端对齐3.3.8 横向布局-自动填充3.3…...

访问者模式:分离数据结构与操作的设计模式
访问者模式:分离数据结构与操作的设计模式 一、模式核心:将操作从数据结构中分离,支持动态添加新操作 在软件开发中,当数据结构(如树、集合)中的元素类型固定,但需要频繁添加新的操作…...

【AI训练环境搭建】在IDE(Pycharm或VSCode)上使用WSL2+Ubuntu22.04+Conda+Tensorflow+GPU进行机器学习训练
本次实践将在IDE(Pycharm或VSCode)上使用WSL2Ubuntu22.04TensorflowGPU进行机器学习训练。基本原理是在IDE中拉起WSL2中的Python解释器,并运行Python程序。要运行CondaTensorflowGPU你可能需要进行以下准备工作。 1. 此示例中将使用一个mnis…...

Leetcode19(亚马逊真题):删除链表的倒是第N个节点
题目分析 删除节点关键:找到被删节点的前一个节点,指针指向 虚拟头节点,方便删除头结点,形成统一操作 为啥要让快指针先行? 我认为更好懂的一种解释:快指针先行n步,这样快慢指针之间形成了一…...

Hadoop+Spark 笔记 2025/4/21
读书笔记 定义 1. 大数据(Big Data) - 指传统数据处理工具难以处理的海量、高速、多样的数据集合,通常具备3V特性(Volume体量大、Velocity速度快、Variety多样性)。扩展后还包括Veracity(真实性&#x…...
千问2.5-VL-7B的推理、微调、部署_笔记2
接上篇:部署千问2.5-VL-7B_笔记1-CSDN博客 这里主要记录微调过程 一、模型微调 这里也使用ms-swift对qwen2.5和qwen2-vl进行自我认知微调和图像OCR微调,并对微调后的模型进行推理。ms-swift是魔搭社区官方提供的LLM工具箱,支持300大语言模…...

Redis从入门到实战基础篇
前言:Redis的安装包含在Redis从入门到实战先导篇中,需要的可移步至此节 目录 1.Redis简单介绍 2.初始Redis 2.1.认识NoSQL 2.2.认识Redis 2.3.安装Redis 3.Redis常见命令 3.1 Redis数据结构 3.2 通用命令 3.3 String命令 3.4 Key的层级结构 3…...
【Docker】在Ubuntu平台上的安装部署
写在前面 docker作为一种部署项目的辅助工具,真是太好用了需要魔法,不然无法正常运行笔者环境:ubuntu22.04 具体步骤 更新系统包索引 sudo apt update安装必要依赖包 sudo apt install -y apt-transport-https ca-certificates curl softwa…...

Java虚拟机(JVM)家族发展史及版本对比
Java虚拟机(JVM)家族发展史及版本对比 一、JVM家族发展史 1. 早期阶段(1996-2000) Classic VM(Java 1.0-1.1): 厂商:Sun Microsystems(Oracle前身)。特点&…...

【学习笔记】Cadence电子设计全流程(三)Capture CIS 原理图绘制(下)
【学习笔记】Cadence电子设计全流程(三)Capture CIS 原理图绘制(下) 3.16 原理图中元件的编辑与更新3.17 原理图元件跳转与查找3.18 原理图常见错误设置于编译检查3.19 低版本原理图文件输出3.20 原理图文件的锁定与解锁3.21 Orca…...
数据库对象与权限管理-Oracle数据字典详解
1. 数据字典概念讲解 Oracle数据字典是数据库的核心组件,它存储了关于数据库结构、用户信息、权限设置和系统性能等重要的元数据信息。这些信息对于数据库的日常管理和维护至关重要。数据字典在数据库创建时自动生成,并随着数据库的运行不断更新。 数据…...
计算机图形学实践:结合Qt和OpenGL实现绘制彩色三角形
在Qt项目中结合OpenGL与CMake需要配置正确的依赖关系、链接库以及代码结构设计。以下是具体实现步骤和关键要点: 一、环境准备 安装Qt 确保安装包含OpenGL模块的Qt版本(如Qt OpenGL、Qt OpenGLWidgets组件)。安装CMake 使用3.10及以上版本&a…...

OpenCV 图形API(54)颜色空间转换-----将图像从 RGB 色彩空间转换到 HSV色彩空间RGB2HSV()
操作系统:ubuntu22.04 OpenCV版本:OpenCV4.9 IDE:Visual Studio Code 编程语言:C11 算法描述 将图像从 RGB 色彩空间转换为 HSV。该函数将输入图像从 RGB 色彩空间转换到 HSV。R、G 和 B 通道值的常规范围是 0 到 255。 输出图像必须是 8 位…...
SpringBoot 封装统一API返回格式对象 标准化开发 请求封装 统一格式处理
统一HTTP请求代码 public class HttpCode {/*** 操作成功*/public static final int SUCCESS 200;/*** 对象创建成功*/public static final int CREATED 201;/*** 请求已经被接受*/public static final int ACCEPTED 202;/*** 操作已经执行成功,但是没有返回数据…...
#git pull 问题:cannot lock ref ‘xxx‘: ref xxx is at (commitID) but expected ‘xxx‘
问题描述:git在拉取远程代码时出现该提示,拉取失败,导致该问题可能是远程有本地没有跟踪过的(小写大写不同)重名的分支,git是不区分大小写的,所以比如有一个的分支原先是example1.0,…...

JavaWeb学习打卡-Day1-分层解耦、Spring IOC、DI
三层架构 Controller(控制层):接收前端发送的请求,对请求进行处理,并响应数据。Service(业务逻辑层):处理具体的业务逻辑。DAO(数据访问层/持久层)ÿ…...
PostgesSQL外部数据封装FDW
PostgesSQL外部数据封装FDW 1. FDW外部数据配置(单表)1.1 远端数据库创建测试表1.2 安装扩展postges\_fdw1.3 创建外部服务SERVER1.4 创建用户映射USER MAPPING1.5 创建远程表FOREIGN TABLE1.6 数据库更新测试 2. FDW外部数据配置(用户&#…...
redis相关问题整理
Redis 支持多种数据类型: 字符串 示例:存储用户信息 // 假设我们使用 redis-plus-plus 客户端库 auto redis Redis("tcp://127.0.0.1:6379"); redis.set("user:1000", "{name: John Doe, email: john.doeexample.com}"…...

基于 Electron、Vue3 和 TypeScript 的辅助创作工具全链路开发方案:涵盖画布系统到数据持久化的完整实现
基于 Electron、Vue3 和 TypeScript 的辅助创作工具全链路开发方案:涵盖画布系统到数据持久化的完整实现 引言 在数字内容创作领域,高效的辅助工具是连接创意与实现的关键桥梁。创作者需要一款集可视化画布、节点关系管理、数据持久化于一体的专业工具&…...