PKCE3-PKCE实现(SpringBoot3.0)
在 Spring Boot 3.0 + JDK 17 的环境下,实现 PKCE 认证的核心步骤包括:
1)引入依赖:使用 Spring Security OAuth 2.0 客户端进行授权码流程。
2)配置 OAuth 2.0 客户端:在 Spring Boot 中配置 OAuth 2.0 客户端,包括设置 code_verifier 和 code_challenge。
3)实现 PKCE 流程:在授权请求中生成 code_challenge,并在换取访问令牌时使用 code_verifier。
1.实现步骤
下面是一个完整的 Spring Boot 3.0 应用实现 PKCE 授权码流程的步骤。
1.1.引入依赖
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.me.mengyu.auth.net</groupId>
<artifactId>mengyu-love</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>war</packaging>
<description>Auth</description>
<dependencies>
<!-- JWT认证利用 -->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>0.11.5</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<version>0.11.5</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId>
<version>0.11.5</version>
</dependency>
<!-- OIDC认证利用 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-oauth2-client</artifactId>
<version>3.0.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>3.0.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
<version>3.0.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
<version>3.0.0</version>
</dependency>
<!-- Spring Security OAuth2 Resource Server -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-oauth2-resource-server</artifactId>
<version>3.0.0</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>3.3.2</version>
<configuration>
<failOnMissingWebXml>false</failOnMissingWebXml>
</configuration>
</plugin>
</plugins>
</build>
</project>
1.2.配置 OAuth2 客户端
server:
port: 8181
spring:
#不同的身份提供者有不同的配置
security:
oauth2:
client:
registration:
my-client:
#client-id, client-secret需要去QQ开发中心获取
client-id: your-client-id
client-secret: your-client-secret
scope: openid, profile, email
redirect-uri: "{baseUrl}/login/oauth2/code/{registrationId}"
authorization-grant-type: authorization_code
provider: my-provider # 指定身份提供者的 ID
provider:
my-provider:
#authorization-uri: https://your-authorization-server.com/auth
#token-uri: https://your-authorization-server.com/token
#user-info-uri: https://your-authorization-server.com/userinfo
#authorization-uri: https://accounts.google.com/o/oauth2/auth
#token-uri: https://oauth2.googleapis.com/token
#user-info-uri: https://openidconnect.googleapis.com/v1/userinfo
#authorization-uri: https://graph.qq.com/oauth2.0/authorize
#token-uri: https://graph.qq.com/oauth2.0/token
#user-info-uri: https://graph.qq.com/oauth2.0/me
#user-info-auth-method: query
# 模拟认证服务器 mengyu-sim-oauth-userserver
authorization-uri: https://graph.qq.com/oauth2.0/authorize
token-uri: https://graph.qq.com/oauth2.0/token
user-info-uri: https://graph.qq.com/oauth2.0/me
#github:
#authorization-uri: https://github.com/login/oauth/authorize
#token-uri: https://github.com/login/oauth/access_token
#user-info-uri: https://api.github.com/user
1.3.PKCE的实现
Spring Security 5.5 及以上版本已经内置了 PKCE 支持,Spring Boot 3.0 采用了 Spring Security 的最新版本,因此我们不需要手动生成 code_verifier 和 code_challenge,而是通过配置和默认行为来完成 PKCE 验证。
1.3.1.配置 Security Filter
在 Spring Boot 3.0 中,默认情况下,OAuth2 客户端支持 PKCE。因此,当客户端是公共客户端时,Spring Security 将自动处理 PKCE 流程。
你需要在 SecurityConfig.java 中配置 Spring Security,使应用程序处理 OAuth 2.0 登录流程:
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.requestMatchers("/", "/login", "/error").permitAll() // 允许所有用户访问的页面
.anyRequest().authenticated() // 其余请求需要认证
.and()
.oauth2Login()
.loginPage("/login") // 自定义登录页
.defaultSuccessUrl("/home", true) // 登录成功后的默认跳转页
.failureUrl("/login?error=true") // 登录失败后的跳转页
.and()
.exceptionHandling()
.authenticationEntryPoint(new LoginUrlAuthenticationEntryPoint("/login")); // 未认证用户访问的处理
return http.build(); // 返回构建的 HttpSecurity
}
这里配置了 OAuth 2.0 登录流程,并允许匿名访问根目录和登录页面。所有其他请求都需要经过认证。
1.3.2.使用DefaultOAuth2AuthorizationRequestResolver 进行 PKCE
默认情况下,Spring Security 使用 DefaultOAuth2AuthorizationRequestResolver 来自动生成 code_challenge 并将其包含在 OAuth2 授权请求中。对于公共客户端(例如浏览器中的单页应用),它会自动应用 PKCE 流程。
1.4.自定义授权请求(可选)
如果需要自定义 PKCE 相关的内容(例如指定 code_challenge_method),可以通过 OAuth2AuthorizationRequestCustomizer 来定制授权请求。
以下是一个自定义 Authorization Request Resolver 的示例,手动配置 code_challenge 和 code_challenge_method:
package com.me.mengyu.love.resolver;
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
import org.springframework.security.oauth2.client.web.DefaultOAuth2AuthorizationRequestResolver;
import org.springframework.security.oauth2.client.web.OAuth2AuthorizationRequestResolver;
import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;
import jakarta.servlet.http.HttpServletRequest;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;
public class CustomOAuth2AuthorizationRequestResolver implements OAuth2AuthorizationRequestResolver {
private final DefaultOAuth2AuthorizationRequestResolver defaultResolver;
// 使用新构造函数,authorizationRequestBaseUri为OAuth2授权端点的基础URI
public CustomOAuth2AuthorizationRequestResolver(ClientRegistrationRepository clientRegistrationRepository) {
this.defaultResolver = new DefaultOAuth2AuthorizationRequestResolver(clientRegistrationRepository, "/oauth2/authorization");
}
//@Override
public OAuth2AuthorizationRequest resolve(HttpServletRequest request) {
OAuth2AuthorizationRequest authorizationRequest = defaultResolver.resolve(request);
return customizeAuthorizationRequest(authorizationRequest);
}
// 覆盖 resolve(HttpServletRequest, String)
//@Override
public OAuth2AuthorizationRequest resolve(HttpServletRequest request, String clientRegistrationId) {
OAuth2AuthorizationRequest authorizationRequest = defaultResolver.resolve(request, clientRegistrationId);
return customizeAuthorizationRequest(authorizationRequest);
}
// Customize the authorization request to include PKCE parameters
private OAuth2AuthorizationRequest customizeAuthorizationRequest(OAuth2AuthorizationRequest authorizationRequest) {
if (authorizationRequest == null) {
return null;
}
// Create a mutable map for additional parameters
Map<String, Object> additionalParameters = new HashMap<String, Object>(authorizationRequest.getAdditionalParameters());
// Generate code_verifier and code_challenge
String codeVerifier = generateCodeVerifier();
String codeChallenge = generateCodeChallenge(codeVerifier);
// Add PKCE parameters to the request
additionalParameters.put("code_challenge", codeChallenge);
additionalParameters.put("code_challenge_method", "S256");
// Create a new authorization request with the PKCE parameters
return OAuth2AuthorizationRequest.from(authorizationRequest)
.additionalParameters(additionalParameters)
.build();
}
// Generate a high-entropy code_verifier
private String generateCodeVerifier() {
SecureRandom secureRandom = new SecureRandom();
byte[] codeVerifierBytes = new byte[32];
secureRandom.nextBytes(codeVerifierBytes);
return Base64.getUrlEncoder().withoutPadding().encodeToString(codeVerifierBytes);
}
// Generate code_challenge from code_verifier using SHA-256
private String generateCodeChallenge(String codeVerifier) {
try {
MessageDigest digest = MessageDigest.getInstance("SHA-256");
byte[] hash = digest.digest(codeVerifier.getBytes(StandardCharsets.UTF_8));
return Base64.getUrlEncoder().withoutPadding().encodeToString(hash);
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException("SHA-256 algorithm not found");
}
}
}
然后在 SecurityConfig 中将这个 CustomOAuth2AuthorizationRequestResolver 注册为授权请求解析器:
public SecurityFilterChain securityFilterChain(HttpSecurity http, ClientRegistrationRepository clientRegistrationRepository) throws Exception {
OAuth2AuthorizationRequestResolver customAuthorizationRequestResolver = new CustomOAuth2AuthorizationRequestResolver(clientRegistrationRepository);
http
.authorizeRequests()
.requestMatchers("/", "/login", "/error").permitAll() // 允许所有用户访问的页面
.anyRequest().authenticated() // 其余请求需要认证
.and()
.oauth2Login()
.loginPage("/login") // 自定义登录页
.defaultSuccessUrl("/home", true) // 登录成功后的默认跳转页
.failureUrl("/login?error=true") // 登录失败后的跳转页
.and()
.exceptionHandling()
.authenticationEntryPoint(new LoginUrlAuthenticationEntryPoint("/login")); // 未认证用户访问的处理
return http.build(); // 返回构建的 HttpSecurity
}
1.5.启动应用
1)启动 Spring Boot 应用。
2)访问 http://localhost:8080,系统将会重定向到 OAuth 2.0 提供的登录页面。
3)完成 OAuth 2.0 登录后,将会使用 PKCE 完成整个授权流程。
1.6.总结
• PKCE 自动化:Spring Security 5.5 及以上版本自动支持 PKCE,因此在大多数情况下不需要手动生成 code_verifier 和 code_challenge。
• 定制化支持:如果需要自定义 PKCE 处理,可以通过 OAuth2AuthorizationRequestResolver 来实现。
相关文章:
PKCE3-PKCE实现(SpringBoot3.0)
在 Spring Boot 3.0 JDK 17 的环境下,实现 PKCE 认证的核心步骤包括: 1)引入依赖:使用 Spring Security OAuth 2.0 客户端进行授权码流程。 2)配置 OAuth 2.0 客户端:在 Spring Boot 中配置 OAuth 2.0 客…...
C++详解vector
目录 构造和拷贝构造 赋值运算符重载: vector的编辑函数: assign函数: push_back和pop_back函数: insert函数: erase函数: swap函数: clear函数: begin函数: e…...
Redis实战--Redis的数据持久化与搭建Redis主从复制模式和搭建Redis的哨兵模式
Redis作为一个高性能的key-value数据库,广泛应用于缓存、消息队列、排行榜等场景。然而,Redis是基于内存的数据库,这意味着一旦服务器宕机,内存中的数据就会丢失。为了解决这个问题,Redis提供了数据持久化的机制&#…...
World of Warcraft [CLASSIC] Engineering 421-440
工程学421-440 World of Warcraft [CLASSIC] Engineering 335-420_魔兽世界宗师级工程学需要多少点-CSDN博客 【萨隆邪铁锭】421-425 学习新技能,其他都不划算,只能做太阳瞄准镜 【太阳瞄准镜】426、427、428、429 【随身邮箱】430 这个基本要做的&am…...
VUE3.5版本解读
官网:Announcing Vue 3.5 | The Vue Point 2024年9月1日,宣布 Vue 3.5“天元突破:红莲螺岩”发布! 反应系统优化 在 3.5 中,Vue 的反应系统经历了另一次重大重构,在行为没有变化的情况下实现了更好的性能…...
spark计算引擎-架构和应用
一Spark 定义:Spark 是一个开源的分布式计算系统,它提供了一个快速且通用的集群计算平台。Spark 被设计用来处理大规模数据集,并且支持多种数据处理任务,包括批处理、交互式查询、机器学习、图形处理和流处理。 核心架构&#x…...
VUE 开发——AJAX学习(二)
一、Bootstrap弹框 功能:不离开当前页面,显示单独内容,供用户操作 步骤: 引入bootstrap.css和bootstrap.js准备弹框标签,确认结构通过自定义属性,控制弹框显示和隐藏 在<head>部分添加:…...
机器学习-KNN分类算法
1.1 KNN分类 KNN分类算法(K-Nearest-Neighbors Classification),又叫K近邻算法。它是概念极其简单,而效果又很优秀的分类算法。1967年由Cover T和Hart P提出。 KNN分类算法的核心思想:如果一个样本在特征空间中的k个最…...
云计算 Cloud Computing
文章目录 1、云计算2、背景3、云计算的特点4、云计算的类型:按提供的服务划分5、云计算的类型:按部署的形式划分 1、云计算 定义: 云计算是一种按使用量付费的模式,这种模式提供可用的、便捷的、按需的网络访问,进入可…...
【算法】DFS 系列之 穷举/暴搜/深搜/回溯/剪枝(上篇)
【ps】本篇有 9 道 leetcode OJ。 目录 一、算法简介 二、相关例题 1)全排列 .1- 题目解析 .2- 代码编写 2)子集 .1- 题目解析 .2- 代码编写 3)找出所有子集的异或总和再求和 .1- 题目解析 .2- 代码编写 4)全排列 II…...
怎么绕开华为纯净模式安装软件
我是标题 众所周不知,华为鸿蒙系统自带纯净模式,而且 没法关闭 : ) 我反正没找到关闭键 以前或许会有提示,无视风险,“仍要安装”。但我这次遇到的问题是,根本没有这个选项,只有“应用市场”和“取消”&…...
CentOS7 离线部署docker和docker-compose环境
一、Docker 离线安装 1. 下载docker tar.gz包 下载地址: Index of linux/static/stable/x86_64/ 本文选择版本:23.0.6 2.创建docker.service文件 vi docker.service文件内容如下: [Unit] DescriptionDocker Application Container Engi…...
Vue 自定义组件实现 v-model 的几种方式
前言 在 Vue 中,v-model 是一个常用的指令,用于实现表单元素和组件之间的双向绑定。当我们使用原生的表单元素时,直接使用 v-model 是很方便的,但是对于自定义组件来说,要实现类似的双向绑定功能就需要一些额外的处理…...
Python Pandas数据处理效率提升指南
大家好,在数据分析中Pandas是Python中最常用的库之一,然而当处理大规模数据集时,Pandas的性能可能会受到限制,导致数据处理变得缓慢。为了提升Pandas的处理速度,可以采用多种优化策略,如数据类型优化、向量…...
最大正方形 Python题解
最大正方形 题目描述 在一个 n m n\times m nm 的只包含 0 0 0 和 1 1 1 的矩阵里找出一个不包含 0 0 0 的最大正方形,输出边长。 输入格式 输入文件第一行为两个整数 n , m ( 1 ≤ n , m ≤ 100 ) n,m(1\leq n,m\leq 100) n,m(1≤n,m≤100),接…...
ubuntu中软件的进程管理-结束软件运行
在Ubuntu系统中,当某个运行中的软件无法正常退出时,可以通过以下几种方法强制结束该软件: 方法一:使用系统监视器(System Monitor)–小白专属 这个相当于win上的资源管理器 打开系统监视器 可以通过点击屏…...
Windows环境部署Oracle 11g
Windows环境部署Oracle 11g 1.安装包下载2. 解压安装包3. 数据库安装3.1 执行安装脚本3.2 电子邮件设置3.3 配置安装选项3.4 配置系统类3.5 选择数据库安装类型3.6 选择安装类型3.7 数据库配置3.8 确认安装信息3.9 设置口令 Oracle常用命令 2023年10月中旬就弄出大致的文章&…...
C语言进阶【8】--联合体和枚举(联合体和枚举这么好用,你不想了解一下吗?)
本章概述 联合体类型的声明联合体的特点联合体的大小的计算枚举类型的声明枚举类型的优点枚举类型的使用枚举类型的大小彩蛋时刻!!! 联合体类型的声明 概述:联合体的关键字为 union。它的结构和结构体是一样的。进行展示…...
Android OTA升级
针对Android系统OTA升级,MTK平台有相关介绍文档:https://online.mediatek.com/apps/faq/detail?faqidFAQ27117&listSW 概念一:OTA包的构建 AOSP full build:Android原生提供的全量包的构建,意思就是可以从任何一…...
【项目经验分享】深度学习自然语言处理技术毕业设计项目案例定制
以下毕业设计是与深度学习自然语言处理(NLP)相关的毕业设计项目案例,涵盖文本分类、生成式模型、语义理解、机器翻译、对话系统、情感分析等多个领域: 实现案例截图: 基于深度学习的文本分类系统基于BERT的情感分析系…...
OptiScaler全攻略:多技术融合实现跨硬件游戏画质增强的创新方案
OptiScaler全攻略:多技术融合实现跨硬件游戏画质增强的创新方案 【免费下载链接】OptiScaler DLSS replacement for AMD/Intel/Nvidia cards with multiple upscalers (XeSS/FSR2/DLSS) 项目地址: https://gitcode.com/GitHub_Trending/op/OptiScaler OptiSc…...
地热模拟实战:当岩石遇上高温水流
Comsol地热开采-热流固耦合(两个模型,均质和裂隙岩体)附赠参考文献。地热开采就像给地球做"针灸",要在不透水的花岗岩里造出人工热储层。最近用COMSOL折腾了两个典型模型:人畜无害的均质岩体和自带裂隙的破碎…...
AI大模型入门必看:小白也能掌握的AI新风口,速收藏!
2026年AI,LLM彻底火出圈了,就连附近的早教中心,都易匾更名,叫“AI智习室”!那LLM究竟是啥? (一)什么是LLM? LLM 是 Large Language Model(大型语言模型)的缩写ÿ…...
Ostrakon-VL-8B效果展示:AI识别货架商品、检查消防通道真实案例
Ostrakon-VL-8B效果展示:AI识别货架商品、检查消防通道真实案例 1. 零售行业的视觉智能革命 走进任何一家超市或餐厅,你都会看到员工忙碌地进行各种检查:商品是否摆放整齐、货架是否需要补货、消防通道是否畅通。这些看似简单的工作&#x…...
如何高效使用PDF-Guru:5种实用PDF处理技巧与完整操作指南
如何高效使用PDF-Guru:5种实用PDF处理技巧与完整操作指南 【免费下载链接】PDF-Guru A Multi-purpose PDF file processing tool with a nice UI that supports merge, split, rotate, reorder, delete, scale, crop, watermark, encrypt/decrypt, bookmark, extrac…...
UiBot调用Python插件报错?可能是运行环境惹的祸(附解决方案)
UiBot调用Python插件报错?深度解析环境冲突与5种高阶解决方案 当你在UiBot中调用精心编写的Python插件时,突然弹出的红色报错信息往往让人措手不及。特别是当代码在本地PyCharm中运行完美,却在UiBot中频频报错时,问题很可能出在环…...
Qwen3-ForcedAligner-0.6B入门必看:start_time为0.00s的边界条件处理
Qwen3-ForcedAligner-0.6B入门必看:start_time为0.00s的边界条件处理 1. 为什么需要关注边界条件 当你使用Qwen3-ForcedAligner-0.6B进行音文对齐时,可能会遇到一个看似简单但很重要的问题:为什么有些词的开始时间是0.00秒?这种…...
Qwen3-ASR-1.7B与QT集成:开发跨平台语音识别桌面应用
Qwen3-ASR-1.7B与QT集成:开发跨平台语音识别桌面应用 1. 引言 想象一下,你正在开发一个需要语音输入功能的桌面应用。传统的语音识别方案要么需要联网调用云端API,要么识别准确率不够理想。现在,有了Qwen3-ASR-1.7B这个强大的开…...
AXI Quad SPI IP核在多主设备环境下的三态总线设计与实现
1. AXI Quad SPI IP核的多主设备挑战 第一次接触AXI Quad SPI IP核的多主设备配置时,我踩过一个典型的坑:两个FPGA内部主模块同时向SPI总线发送数据,导致MOSI信号出现毛刺。这种情况在共享总线架构中非常常见,而三态总线设计正是解…...
Mastering nohup: Redirecting Output for Persistent Server Deployments
1. 为什么你需要掌握nohup命令 想象一下这个场景:你在远程服务器上启动了一个重要的Java服务,花了半小时调试终于跑起来了。这时候老板喊你开会,你顺手关闭了终端窗口。等会议结束回来一看——服务居然挂了!所有努力付诸东流&…...
