SpringBoot3集成Oauth2——1(/oauth2/token方法的升级踩坑)

news2025/5/13 1:13:50

备注:本文适用于你在SpringBoot2.7以前集成过oauth2,并且项目已经正式投入使用的情况,否则,我建议你直接学习或者找资料学习最新的oauth2集成,就不要纠结于老版本的oauth2。

原因:Spring Security 5.x和Spring Security6.x,我个人认为,你可以理解为两套不同的框架,他们仅仅只是名字差不多而已,升级难度在于,旧系统的登录认证已经在使用了,尤其是已经介入很多子系统时,这时候需升级需要适配原来的认证,不然会导致子系统需要重新单点登录。

Spring Security 5.x → 6.x:
弃用旧 API:spring-security-oauth2-autoconfigure 被移除,取而代之的是更模块化的组件(如 spring-boot-starter-oauth2-client)。
新认证架构:基于 SecurityFilterChain 和 AuthenticationProvider 的声明式配置取代了旧的 WebSecurityConfigurerAdapter。

本文部分内容参考:https://blog.csdn.net/gandilong 写的SpringBoot+SpringSecurity OAuth2 认证服务搭建实战 (二)

文章目录

  • 1快速demo 授权服务器
    • 1.1依赖
    • 1.2AuthorizationServerConfig
    • 1.3SecurityConfig
    • 1.4验证
  • 2老版本/oauth/token
  • 3问题:认证接口变了
  • 4新版本/oauth2/token
    • 4.1定位/oauth2/token

1快速demo 授权服务器

1.1依赖

springBoot版本

   <parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>3.4.4</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-oauth2-authorization-server</artifactId>
		</dependency>

通过查看依赖,发现,SpringBoot3.4.4引用如下依赖

<dependency>
      <groupId>org.springframework.security</groupId>
      <artifactId>spring-security-oauth2-authorization-server</artifactId>
      <version>1.3.0</version>
      <scope>compile</scope>
    </dependency>

1.2AuthorizationServerConfig

import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.util.UUID;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.core.AuthorizationGrantType;
import org.springframework.security.oauth2.core.ClientAuthenticationMethod;
import org.springframework.security.oauth2.core.oidc.OidcScopes;
import org.springframework.security.oauth2.jwt.JwtDecoder;
import org.springframework.security.oauth2.jwt.JwtEncoder;
import org.springframework.security.oauth2.jwt.NimbusJwtEncoder;
import org.springframework.security.oauth2.server.authorization.client.InMemoryRegisteredClientRepository;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClient;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;
import org.springframework.security.oauth2.server.authorization.config.annotation.web.configuration.OAuth2AuthorizationServerConfiguration;
import org.springframework.security.oauth2.server.authorization.config.annotation.web.configurers.OAuth2AuthorizationServerConfigurer;
import org.springframework.security.oauth2.server.authorization.settings.AuthorizationServerSettings;
import org.springframework.security.oauth2.server.authorization.web.OAuth2ClientAuthenticationFilter;
import org.springframework.security.oauth2.server.authorization.web.OAuth2TokenEndpointFilter;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;

import com.nimbusds.jose.jwk.JWKSet;
import com.nimbusds.jose.jwk.RSAKey;
import com.nimbusds.jose.jwk.source.ImmutableJWKSet;
import com.nimbusds.jose.jwk.source.JWKSource;
import com.nimbusds.jose.proc.SecurityContext;

@Configuration
@Import(OAuth2AuthorizationServerConfiguration.class)
public class AuthorizationServerConfig {

	OAuth2TokenEndpointFilter  OAuth2TokenEndpointFilter ;
	
	OAuth2AuthorizationServerConfigurer  OAuth2AuthorizationServerConfigurer;
	
	OAuth2ClientAuthenticationFilter  OAuth2ClientAuthenticationFilter ;
	
	@Bean
    public PasswordEncoder passwordEncoder() throws NoSuchAlgorithmException {
		return new BCryptPasswordEncoder(12,SecureRandom.getInstanceStrong());
    }
	
	/**
	 * 提供登录页面用户名密码的认证
	 * @return
	 */
	@Bean
	public UserDetailsService userDetailsService(PasswordEncoder passwdEncoder) {
        UserDetails user= User.builder()
                .username("user")
                //.password(passwdEncoder.encode("123")) // 使用 {noop} 前缀表示密码不会被编码
                .password("{noop}123") // 使用 {noop} 前缀表示密码不会被编码
                .accountExpired(false)
                .credentialsExpired(false)
                .accountLocked(false)
                .authorities("ROLE_USER") // 用户的权限
                .build();

		return new InMemoryUserDetailsManager(user);
	}
	
	/**
	 * 应用注册仓库
	 * @return
	 */
	@Bean
	public RegisteredClientRepository registeredClientRepository(PasswordEncoder passwdEncoder) {
		
		RegisteredClient oidcClient = RegisteredClient.withId(UUID.randomUUID().toString())
				.clientId("clientid")
				.clientSecret(passwdEncoder.encode("client_secret"))如果是CLIENT_SECRET_POST才会用到
				.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)
				.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_POST)
				.clientAuthenticationMethod(ClientAuthenticationMethod.NONE)
				.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
				.authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS)
				.authorizationGrantType(AuthorizationGrantType.REFRESH_TOKEN)
				.redirectUri("http://localhost:8080/login/oauth2/code/clientid")
				.postLogoutRedirectUri("http://localhost:8080/")
				.scope(OidcScopes.OPENID)
				.scope(OidcScopes.PROFILE)
				.build();

		return new InMemoryRegisteredClientRepository(oidcClient);
	}
	
	/**
	 * 生成jwk,用在jwt编码和jwt解码器上
	 * @return
	 */
	@Bean
	public JWKSource<SecurityContext> jwkSource() {
		KeyPair keyPair = generateRsaKey();
		RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
		RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
		RSAKey rsaKey = new RSAKey.Builder(publicKey)
				.privateKey(privateKey)
				.keyID(UUID.randomUUID().toString())
				.build();
		JWKSet jwkSet = new JWKSet(rsaKey);
		return new ImmutableJWKSet<>(jwkSet);
	}

	/**
	 * 生成RSA256非对称的秘钥对:公钥和私钥,其中公钥会出布出去。
	 * @return
	 */
	private static KeyPair generateRsaKey() {
		KeyPair keyPair;
		try {
			KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
			keyPairGenerator.initialize(2048);
			keyPair = keyPairGenerator.generateKeyPair();
		}
		catch (Exception ex) {
			throw new IllegalStateException(ex);
		}
		return keyPair;
	}

	/**
	 * jwt 解码器,给资源服务器用
	 * @param jwkSource
	 * @return
	 */
	@Bean
	public JwtDecoder jwtDecoder(JWKSource<SecurityContext> jwkSource) {
		return OAuth2AuthorizationServerConfiguration.jwtDecoder(jwkSource);
	}
	
	/**
	 * jwt 编码器,给授权服务器用
	 * @param jwkSource
	 * @return
	 */
	@Bean
	public JwtEncoder jwtEncoder(JWKSource<SecurityContext> jwkSource) {
		return new NimbusJwtEncoder(jwkSource);
	}

	/**
	 * 默认授权服务器配置
	 * @return
	 */
	@Bean
	public AuthorizationServerSettings authorizationServerSettings() {
		return AuthorizationServerSettings.builder().build();
	}
}

1.3SecurityConfig

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.web.SecurityFilterChain;


@Configuration
@EnableWebSecurity
public class SecurityConfig {

	@Bean
	@Order(1)
	public SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http)
			throws Exception {
		http
		.authorizeHttpRequests((authorize) -> authorize.anyRequest().authenticated())
		.oauth2ResourceServer(resourceServer -> resourceServer.jwt(Customizer.withDefaults()));
		return http.build();
	}

}

1.4验证

需要注意,这里使用post + x-www-form-urlencoded来请求,这个是区别于老版本(大概应该是boot2.7以前)
在这里插入图片描述
如果你在springBoot2.7以前用过oauth2,对于上述的方式肯定会很蒙蔽,接下来我们来说明为啥会这样。

2老版本/oauth/token

如下为例,下面是我们一个老版本的oauth2的登录认证接口,这里我用密码模式来举例

http://127.0.0.1:8080/oauth/token?client_id=client_hutao&client_secret=secret_hutao&username=hutao&password=123456&grant_type=password
我们使用postMan调用,如下所示
在这里插入图片描述
两者一对比就能发现,老版在url里面传参数,新版在请求体x-www-form-urlencoded传参数。
老版oauth2使用我们比较熟悉的Spring框架的接口开发方式来实现,如下代码所示

@RequestMapping(value = "/oauth/token", method=RequestMethod.GET)

@RequestMapping(value = "/oauth/token", method=RequestMethod.POST)

@RequestParam Map<String, String> parameters

因此我们想要去阅读源码或者调试信息时,只需要对org.springframework.security.oauth2.provider.endpoint.TokenEndpoint打debug打断点就可以了,该代码阅读起来难度也比较小,很贴近我们比较常见的restful接口的开发。
在这里插入图片描述

3问题:认证接口变了

我们先抛开我们怎么搭建授权服务器的问题,以及我现在为什么要从springBoot2升级到springBoot3,先解决一个问题,那就是,我们做授权服务器来实现单点登录这些功能,为啥要这样做?因为我们有很多系统都会调用这个单点登录来实现登录。也就是说,我们的这个登录认证接口是被很系统正在使用的,现在如果直接就升级,会导致我们很多子系统要跟着改,这影响很大,因此,我们从springBoot2升级到springBoot3(没有商量,必须升级)以后,oauth2也得跟着升级,我们得保证oauth2跟着升级以后,我们在新版本的oauth2下,得适配以前其他系统对接的接口。

4新版本/oauth2/token

通过上面的案例,我们发现接口地址变了,以前是/oauth/token,现在是/oauth2/token,并且
现在TokenEndpoint它不见了,不是说换个名字,是正儿八经的被删除了,通过追踪发现,/oauth2/token的工作原理机制和/oauth/token完全不一样。

特性旧版 (Spring Security OAuth2)新版 (Spring Authorization Server)
依赖spring-security-oauth2(已废弃)spring-boot-starter-oauth2-authorization-server
端点类TokenEndpoint(显式 @FrameworkEndpoint)无集中式端点类,改为分散的 Filter + AuthenticationProvider OAuth2TokenEndpointFilter
请求方法支持 GET/POST /oauth/token仅支持 POST /oauth2/token
代码入口直接由 TokenEndpoint 处理通过过滤器链和认证提供者协作处理

4.1定位/oauth2/token

如下所示,当我们请求这个接口时,实际上是下面这个OAuth2TokenEndpointFilter处理,而不是以前的那种,TokenEndpoint中的@RequestMapping(value = “/oauth/token”)处理
在这里插入图片描述
如下所示,你就可以尽情的debug,查看请求参数或者报错信息,来帮助你定位问题了
在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

基于Qt开发的多线程TCP服务端

目录 一、Qt TCP服务端开发环境准备1. 项目配置2. 核心类说明 二、服务端搭建步骤详解步骤1&#xff1a;初始化服务端对象步骤2&#xff1a;启动端口监听步骤3&#xff1a;处理客户端连接 三、数据通信与状态管理1. 数据收发实现2. 客户端状态监控 四、进阶功能扩展1. 多客户端…

Centos离线安装mysql、redis、nginx等工具缺乏层层依赖的解决方案

Centos离线安装mysql、redis、nginx等工具缺乏层层依赖的解决方案 引困境yum-utils破局 引 前段时间&#xff0c;有个项目有边缘部署的需求&#xff0c;一台没有的外网的Centos系统服务器&#xff0c;需要先安装jdk&#xff0c;node&#xff0c;mysql&#xff0c;reids&#xf…

从零开始开发纯血鸿蒙应用之XML解析

从零开始开发纯血鸿蒙应用 〇、前言一、鸿蒙SDK中的 XML API1、ohos.xml2、ohos.convertxml 三、XML 解析实践1、源数据结构2、定义映射关系3、定义接收对象4、获取文章信息 四、总结 〇、前言 在前后端的数据传输方面&#xff0c;论格式化形式&#xff0c;JSON格式自然是首选…

10.王道_HTTP

1. 互联网时代的诞生 2. HTTP的基本特点 2.1客户端-服务端模型 2.2 无状态协议 2.3 可靠性 2.4 文本协议 3. HTML,CSS和JS 4. HTTP的各个组件 4.1 客户端 4.2 服务端 4.3 代理 5. URI和URL 6. HTTP报文 HTTP报文分为两种——请求报文和响应报文。 6.1 GET请求示例 注意&#…

解决stm32HAL库使用vscode打开,识别不到头文件及uint8_t等问题

解决stm32HAL库使用vscode打开&#xff0c;识别不到头文件及uint8_t等问题 结论&#xff0c;问题有2问题1问题2解决办法将Keil Assistant自动生成的.vscode目录复制到MDK-ARM上层目录将Keil Assistant自动生成的.vscode目录复制到MDK-ARM上层目录将Keil Assistant自动生成的.vs…

uniapp-商城-50-后台 商家信息(输入进行自定义规则验证)

本文介绍了如何在后台管理系统中添加和展示商家信息&#xff0c;包括商家logo、名称、电话、地址和介绍等内容&#xff0c;并支持后期上传营业许可等文件。通过使用uni-app的uni-forms组件&#xff0c;可以方便地实现表单的创建、校验和管理操作。文章详细说明了组件的引入、页…

网页版部署MySQL + Qwen3-0.5B + Flask + Dify 工作流部署指南

1. 安装MySQL和PyMySQL 安装MySQL # 在Ubuntu/Debian上安装 sudo apt update sudo apt install mysql-server sudo mysql_secure_installation# 启动MySQL服务 sudo systemctl start mysql sudo systemctl enable mysql 安装PyMySQL pip install pymysql 使用 apt 安装 My…

WEBSTORM前端 —— 第2章:CSS —— 第8节:网页制作2(小兔鲜儿)

目录 1.项目目录 2.SEO 三大标签 3.Favicon 图标 4.版心 5.快捷导航(shortcut) 6.头部(header) 7.底部(footer) 8.banner 9.banner – 圆点 10.新鲜好物(goods) 11.热门品牌(brand) 12.生鲜(fresh) 13.最新专题(topic) 1.项目目录 【xtx-pc】 ima…

仓储车间安全革命:AI叉车防撞装置系统如何化解操作风险

在现代物流体系中&#xff0c;仓储承担着货物储存、保管、分拣和配送等重要任务。但现代仓储行业的安全现状却不容乐观&#xff0c;诸多痛点严重制约着其发展&#xff0c;其中叉车作业的安全问题尤为突出。相关数据显示&#xff0c;全球范围内&#xff0c;每年因叉车事故导致的…

修改图像分辨率

在这个教程中&#xff0c;您将学习如何使用Python和深度学习技术来调整图像的分辨率。我们将从基础的图像处理技术开始&#xff0c;逐步深入到使用预训练的深度学习模型进行图像超分辨率处理。 一、常规修改方法 1. 安装Pillow库 首先&#xff0c;你需要确保你的Python环境中…

Redis 主从同步与对象模型(四)

目录 1.淘汰策略 1.1 expire/pexpire&#xff08;设置键的过期时间&#xff09; 1.2 配置 1.maxmemory 2.maxmemory-policy 3.maxmemory-samples 2.持久化 2.1背景 2.2 fork 的写时复制机制 2.3 大 key 3.持久化方式 3.1 aof&#xff08;Apped Only File&#xff09…

Linux系列:如何用perf跟踪.NET程序的mmap泄露

一&#xff1a;背景 1. 讲故事 如何跟踪.NET程序的mmap泄露&#xff0c;这个问题困扰了我差不多一年的时间&#xff0c;即使在官方的github库中也找不到切实可行的方案&#xff0c;更多海外大佬只是推荐valgrind这款工具&#xff0c;但这款工具底层原理是利用模拟器&#xff…

如何租用服务器并通过ssh连接远程服务器终端

这里我使用的是智算云扉 没有打广告 但确实很便宜 还有二十小时免费额度 链接如下 注册之后 租用新实例 选择操作系统 选择显卡型号 点击租用 选择计费方式 选择镜像 如果跑深度学习的话 就选项目对应的torch版本 没有的话 就创建纯净的cuda 自己安装 点击创建实例 创建之后 …

华为设备链路聚合实验:网络工程实战指南

链路聚合就像为网络搭建 “并行高速路”&#xff0c;既能扩容带宽&#xff0c;又能保障链路冗余&#xff0c;超实用&#xff01; 一、实验拓扑速览 图中两台交换机 LSW1 和 LSW2&#xff0c;PC1、PC2 归属 VLAN 10&#xff0c;PC3 归属 VLAN 30。LSW1 与 LSW2 通过 GE0/0/1、…

AUTOSAR图解==>AUTOSAR_TR_AIDesignPatternsCatalogue

AUTOSAR 人工智能设计模式目录 AUTOSAR传感器执行器与仲裁设计模式的深入解析与图解 目录 简介传感器和执行器模式 架构概述组件结构交互流程应用场景 多请求者或提供者之间的仲裁模式 架构概述组件结构仲裁流程应用场景 总结 1. 简介 AUTOSAR&#xff08;AUTomotive Open Sy…

双系统电脑中如何把ubuntu装进外接移动固态硬盘

电脑&#xff1a;win11 ubuntu22.04 实体机 虚拟机&#xff1a;VMware17 镜像文件&#xff1a;ubuntu-22.04.4-desktop-amd64.iso 或者 ubuntu20.4的镜像 外接固态硬盘1个 一、首先win11中安装vmware17 具体安装方法&#xff0c;网上很多教程 二、磁盘分区 1.在笔…

【C语言】程序的预处理,#define详解

一、预定义符号 二、#define 1.#define定义标识符 #define &#xff0b; 自定义名称 &#xff0b; 代替的内容 例&#xff1a; #define MAX 100 #define CASE break;case #define CASE break;caseint main() {int n 0;switch (n){case 1:CASE 2:CASE 3:CASE 4:}return …

NVM完全指南:安装、配置与最佳实践

发布于 2025年5月7日 • 阅读时间&#xff1a;10分钟 &#x1f4a1; TL;DR: 本文详细介绍了如何完整卸载旧版Node.js&#xff0c;安装NVM&#xff0c;配置阿里云镜像源&#xff0c;以及设置node_global与node_cache目录&#xff0c;打造高效Node.js开发环境。 &#x1f4cb; 目…

(二)毛子整洁架构(CQRS/Dapper/领域事件处理器/垂直切片)

文章目录 项目地址一、Application 层1.1 定义CQRS的接口以及其他服务1. Command2. IQuery查询3. 当前时间服务接口4. 邮件发送服务接口 1.2 ReserveBooking Command1. 处理传入的参数2. ReserveBookingCommandHandler3. BookingReservedDomainEvent 1.3 Query使用Sql查询1. 创…

如何修改MySQL数据库密码

文章目录 一、忘记数据库密码该如何修改1. 关闭数据库的服务2.跳过安全检查3. 重置密码4.查询用户是否存在5.退出验证密码是否正确 二、未忘记密码该如何修改密码1.直接修改密码2.登录mysql 时间久了&#xff0c;忘记数据库密码了。。。。。 一、忘记数据库密码该如何修改 1. …