当前位置: 首页 > news >正文

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 的环境下&#xff0c;实现 PKCE 认证的核心步骤包括&#xff1a; 1&#xff09;引入依赖&#xff1a;使用 Spring Security OAuth 2.0 客户端进行授权码流程。 2&#xff09;配置 OAuth 2.0 客户端&#xff1a;在 Spring Boot 中配置 OAuth 2.0 客…...

C++详解vector

目录 构造和拷贝构造 赋值运算符重载&#xff1a; vector的编辑函数&#xff1a; assign函数&#xff1a; push_back和pop_back函数&#xff1a; insert函数&#xff1a; erase函数&#xff1a; swap函数&#xff1a; clear函数&#xff1a; begin函数&#xff1a; e…...

Redis实战--Redis的数据持久化与搭建Redis主从复制模式和搭建Redis的哨兵模式

Redis作为一个高性能的key-value数据库&#xff0c;广泛应用于缓存、消息队列、排行榜等场景。然而&#xff0c;Redis是基于内存的数据库&#xff0c;这意味着一旦服务器宕机&#xff0c;内存中的数据就会丢失。为了解决这个问题&#xff0c;Redis提供了数据持久化的机制&#…...

World of Warcraft [CLASSIC] Engineering 421-440

工程学421-440 World of Warcraft [CLASSIC] Engineering 335-420_魔兽世界宗师级工程学需要多少点-CSDN博客 【萨隆邪铁锭】421-425 学习新技能&#xff0c;其他都不划算&#xff0c;只能做太阳瞄准镜 【太阳瞄准镜】426、427、428、429 【随身邮箱】430 这个基本要做的&am…...

VUE3.5版本解读

官网&#xff1a;Announcing Vue 3.5 | The Vue Point 2024年9月1日&#xff0c;宣布 Vue 3.5“天元突破&#xff1a;红莲螺岩”发布&#xff01; 反应系统优化 在 3.5 中&#xff0c;Vue 的反应系统经历了另一次重大重构&#xff0c;在行为没有变化的情况下实现了更好的性能…...

spark计算引擎-架构和应用

一Spark 定义&#xff1a;Spark 是一个开源的分布式计算系统&#xff0c;它提供了一个快速且通用的集群计算平台。Spark 被设计用来处理大规模数据集&#xff0c;并且支持多种数据处理任务&#xff0c;包括批处理、交互式查询、机器学习、图形处理和流处理。 核心架构&#x…...

VUE 开发——AJAX学习(二)

一、Bootstrap弹框 功能&#xff1a;不离开当前页面&#xff0c;显示单独内容&#xff0c;供用户操作 步骤&#xff1a; 引入bootstrap.css和bootstrap.js准备弹框标签&#xff0c;确认结构通过自定义属性&#xff0c;控制弹框显示和隐藏 在<head>部分添加&#xff1a…...

机器学习-KNN分类算法

1.1 KNN分类 KNN分类算法&#xff08;K-Nearest-Neighbors Classification&#xff09;&#xff0c;又叫K近邻算法。它是概念极其简单&#xff0c;而效果又很优秀的分类算法。1967年由Cover T和Hart P提出。 KNN分类算法的核心思想&#xff1a;如果一个样本在特征空间中的k个最…...

云计算 Cloud Computing

文章目录 1、云计算2、背景3、云计算的特点4、云计算的类型&#xff1a;按提供的服务划分5、云计算的类型&#xff1a;按部署的形式划分 1、云计算 定义&#xff1a; 云计算是一种按使用量付费的模式&#xff0c;这种模式提供可用的、便捷的、按需的网络访问&#xff0c;进入可…...

【算法】DFS 系列之 穷举/暴搜/深搜/回溯/剪枝(上篇)

【ps】本篇有 9 道 leetcode OJ。 目录 一、算法简介 二、相关例题 1&#xff09;全排列 .1- 题目解析 .2- 代码编写 2&#xff09;子集 .1- 题目解析 .2- 代码编写 3&#xff09;找出所有子集的异或总和再求和 .1- 题目解析 .2- 代码编写 4&#xff09;全排列 II…...

怎么绕开华为纯净模式安装软件

我是标题 众所周不知&#xff0c;华为鸿蒙系统自带纯净模式&#xff0c;而且 没法关闭 : ) 我反正没找到关闭键 以前或许会有提示&#xff0c;无视风险&#xff0c;“仍要安装”。但我这次遇到的问题是&#xff0c;根本没有这个选项&#xff0c;只有“应用市场”和“取消”&…...

CentOS7 离线部署docker和docker-compose环境

一、Docker 离线安装 1. 下载docker tar.gz包 下载地址&#xff1a; Index of linux/static/stable/x86_64/ 本文选择版本&#xff1a;23.0.6 2.创建docker.service文件 vi docker.service文件内容如下&#xff1a; [Unit] DescriptionDocker Application Container Engi…...

Vue 自定义组件实现 v-model 的几种方式

前言 在 Vue 中&#xff0c;v-model 是一个常用的指令&#xff0c;用于实现表单元素和组件之间的双向绑定。当我们使用原生的表单元素时&#xff0c;直接使用 v-model 是很方便的&#xff0c;但是对于自定义组件来说&#xff0c;要实现类似的双向绑定功能就需要一些额外的处理…...

Python Pandas数据处理效率提升指南

大家好&#xff0c;在数据分析中Pandas是Python中最常用的库之一&#xff0c;然而当处理大规模数据集时&#xff0c;Pandas的性能可能会受到限制&#xff0c;导致数据处理变得缓慢。为了提升Pandas的处理速度&#xff0c;可以采用多种优化策略&#xff0c;如数据类型优化、向量…...

最大正方形 Python题解

最大正方形 题目描述 在一个 n m n\times m nm 的只包含 0 0 0 和 1 1 1 的矩阵里找出一个不包含 0 0 0 的最大正方形&#xff0c;输出边长。 输入格式 输入文件第一行为两个整数 n , m ( 1 ≤ n , m ≤ 100 ) n,m(1\leq n,m\leq 100) n,m(1≤n,m≤100)&#xff0c;接…...

ubuntu中软件的进程管理-结束软件运行

在Ubuntu系统中&#xff0c;当某个运行中的软件无法正常退出时&#xff0c;可以通过以下几种方法强制结束该软件&#xff1a; 方法一&#xff1a;使用系统监视器&#xff08;System Monitor&#xff09;–小白专属 这个相当于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】--联合体和枚举(联合体和枚举这么好用,你不想了解一下吗?)

本章概述 联合体类型的声明联合体的特点联合体的大小的计算枚举类型的声明枚举类型的优点枚举类型的使用枚举类型的大小彩蛋时刻&#xff01;&#xff01;&#xff01; 联合体类型的声明 概述&#xff1a;联合体的关键字为 union。它的结构和结构体是一样的。进行展示&#xf…...

Android OTA升级

针对Android系统OTA升级&#xff0c;MTK平台有相关介绍文档&#xff1a;https://online.mediatek.com/apps/faq/detail?faqidFAQ27117&listSW 概念一&#xff1a;OTA包的构建 AOSP full build&#xff1a;Android原生提供的全量包的构建&#xff0c;意思就是可以从任何一…...

【项目经验分享】深度学习自然语言处理技术毕业设计项目案例定制

以下毕业设计是与深度学习自然语言处理&#xff08;NLP&#xff09;相关的毕业设计项目案例&#xff0c;涵盖文本分类、生成式模型、语义理解、机器翻译、对话系统、情感分析等多个领域&#xff1a; 实现案例截图&#xff1a; 基于深度学习的文本分类系统基于BERT的情感分析系…...

Redis相关知识总结(缓存雪崩,缓存穿透,缓存击穿,Redis实现分布式锁,如何保持数据库和缓存一致)

文章目录 1.什么是Redis&#xff1f;2.为什么要使用redis作为mysql的缓存&#xff1f;3.什么是缓存雪崩、缓存穿透、缓存击穿&#xff1f;3.1缓存雪崩3.1.1 大量缓存同时过期3.1.2 Redis宕机 3.2 缓存击穿3.3 缓存穿透3.4 总结 4. 数据库和缓存如何保持一致性5. Redis实现分布式…...

镜像里切换为普通用户

如果你登录远程虚拟机默认就是 root 用户&#xff0c;但你不希望用 root 权限运行 ns-3&#xff08;这是对的&#xff0c;ns3 工具会拒绝 root&#xff09;&#xff0c;你可以按以下方法创建一个 非 root 用户账号 并切换到它运行 ns-3。 一次性解决方案&#xff1a;创建非 roo…...

Java-41 深入浅出 Spring - 声明式事务的支持 事务配置 XML模式 XML+注解模式

点一下关注吧&#xff01;&#xff01;&#xff01;非常感谢&#xff01;&#xff01;持续更新&#xff01;&#xff01;&#xff01; &#x1f680; AI篇持续更新中&#xff01;&#xff08;长期更新&#xff09; 目前2025年06月05日更新到&#xff1a; AI炼丹日志-28 - Aud…...

Linux云原生安全:零信任架构与机密计算

Linux云原生安全&#xff1a;零信任架构与机密计算 构建坚不可摧的云原生防御体系 引言&#xff1a;云原生安全的范式革命 随着云原生技术的普及&#xff0c;安全边界正在从传统的网络边界向工作负载内部转移。Gartner预测&#xff0c;到2025年&#xff0c;零信任架构将成为超…...

【配置 YOLOX 用于按目录分类的图片数据集】

现在的图标点选越来越多&#xff0c;如何一步解决&#xff0c;采用 YOLOX 目标检测模式则可以轻松解决 要在 YOLOX 中使用按目录分类的图片数据集&#xff08;每个目录代表一个类别&#xff0c;目录下是该类别的所有图片&#xff09;&#xff0c;你需要进行以下配置步骤&#x…...

Rust 异步编程

Rust 异步编程 引言 Rust 是一种系统编程语言,以其高性能、安全性以及零成本抽象而著称。在多核处理器成为主流的今天,异步编程成为了一种提高应用性能、优化资源利用的有效手段。本文将深入探讨 Rust 异步编程的核心概念、常用库以及最佳实践。 异步编程基础 什么是异步…...

让AI看见世界:MCP协议与服务器的工作原理

让AI看见世界&#xff1a;MCP协议与服务器的工作原理 MCP&#xff08;Model Context Protocol&#xff09;是一种创新的通信协议&#xff0c;旨在让大型语言模型能够安全、高效地与外部资源进行交互。在AI技术快速发展的今天&#xff0c;MCP正成为连接AI与现实世界的重要桥梁。…...

AGain DB和倍数增益的关系

我在设置一款索尼CMOS芯片时&#xff0c;Again增益0db变化为6DB&#xff0c;画面的变化只有2倍DN的增益&#xff0c;比如10变为20。 这与dB和线性增益的关系以及传感器处理流程有关。以下是具体原因分析&#xff1a; 1. dB与线性增益的换算关系 6dB对应的理论线性增益应为&…...

20个超级好用的 CSS 动画库

分享 20 个最佳 CSS 动画库。 它们中的大多数将生成纯 CSS 代码&#xff0c;而不需要任何外部库。 1.Animate.css 一个开箱即用型的跨浏览器动画库&#xff0c;可供你在项目中使用。 2.Magic Animations CSS3 一组简单的动画&#xff0c;可以包含在你的网页或应用项目中。 3.An…...

Python+ZeroMQ实战:智能车辆状态监控与模拟模式自动切换

目录 关键点 技术实现1 技术实现2 摘要&#xff1a; 本文将介绍如何利用Python和ZeroMQ消息队列构建一个智能车辆状态监控系统。系统能够根据时间策略自动切换驾驶模式&#xff08;自动驾驶、人工驾驶、远程驾驶、主动安全&#xff09;&#xff0c;并通过实时消息推送更新车…...