spring-authorization-server 公共客户端方式获取授权码和Token的流程

spring-authorization-serve【版本1.2.1】官方文档中提及了关于RegisteredClient中所涉及的客户端身份验证方法,也就是RegisteredClient中提及的clientAuthenticationMethods属性对应的“none”值,目前clientAuthenticationMethods属性支持的值包含:client_secret_basic, client_secret_post, private_key_jwt, client_secret_jwt, and none (public clients)。
大家可参考官方文档来了解PKCE相关的标准规范定义。这里我推荐大家看一下附录B,如何通过code_verifier获取code_challenge。

PKCE Protocol Flow

通过code_verifier获取code_challenge

这里直接引用官方文档附录B示例如下:

客户端使用合适的随机数生成器创建出来32个八位字节序列。本例中表示值的八位字节(使用JSON数组表示法)为:

[116, 24, 223, 180, 151, 153, 224, 37, 79, 250, 96, 125, 216, 173, 187, 186, 22, 212, 37, 77, 105, 214, 191, 240, 91, 88, 5, 88, 83, 132, 141, 121]

将此八位字节序列进行base64url编码作为code_verifier的值:dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk

然后通过SHA256哈希函数对code_verifier进行哈希,生成:

[19, 211, 30, 150, 26, 26, 216, 236, 47, 22, 177, 12, 76, 152, 46, 8, 118, 168, 120, 173, 109, 241, 68, 86, 110, 225, 137, 74, 203, 112, 249, 195]

将该八位字节序列base64url编码作为code_challenge的值:E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM

最后在授权请求的url中追加如下参数code_challengecode_challenge_method

code_challenge=E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM&code_challenge_method=S256

code_verifier和code_challenge的具体用法

下面在是PKCE官网提供的Abstract Protocol Flow如下:
在这里插入图片描述
A、客户端在请求授权服务的Authorization Endpoint (授权端点)获取Authorization Code(授权码)时,需要提交的参数需要包含“t(code_verifier)”,和“t_m”,“t(code_verifier)”也就是上面说的code_challenge,“t_m”就是code_challenge_method。这里有个操作是授权服务会把code_challenge和code_challenge_method存储起来。
B、授权服务返回Authorization Code(授权码)给客户端
C、客户端在请求授权服务的Token Endpoint获取token时需要提交参数code_verifier,授权服务会根据此次提交的code_verifier和A步存储的code_challenge_method生成code_challenge来验证是否和A步存储的code_challenge一致。
D、code_challenge一致则进行下面的流程直至成功返回Access Token。

在spring-authorization-server的运用

spring-authorization-server针对公共客户端也是依据PKCE的标准进行处理的。

创建应用程序

在获取授权码前首先需要创建一个SpringBoot应用程序可参考官网的Getting Started,然后稍作修改,如下:

application.yml

server:port: 6001logging:level:org.springframework.security: tracespring:security:oauth2:authorizationserver:client:public-client:registration:client-id: "my-app"client-authentication-methods:- "none"authorization-grant-types:- "authorization_code"- "refresh_token"redirect-uris:- "http://127.0.0.1:6001/oauth2/code"post-logout-redirect-uris:- "http://127.0.0.1:6001/"scopes:- "user_info"- "openid"- "profile"- "client.create"require-authorization-consent: true

SecurityConfig.java

@Configuration
@EnableWebSecurity
public class SecurityConfig {@Bean@Order(1)public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http)throws Exception {OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(http);http.getConfigurer(OAuth2AuthorizationServerConfigurer.class).oidc(Customizer.withDefaults());	// Enable OpenID Connect 1.0http// Redirect to the login page when not authenticated from the// authorization endpoint.exceptionHandling((exceptions) -> exceptions.defaultAuthenticationEntryPointFor(new LoginUrlAuthenticationEntryPoint("/login"),new MediaTypeRequestMatcher(MediaType.TEXT_HTML)))// Accept access tokens for User Info and/or Client Registration.oauth2ResourceServer((resourceServer) -> resourceServer.jwt(Customizer.withDefaults()));return http.build();}@Bean@Order(2)public SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http)throws Exception {http.authorizeHttpRequests((authorize) -> authorize.anyRequest().authenticated())// Form login handles the redirect to the login page from the// authorization server filter chain.formLogin(Customizer.withDefaults());return http.build();}@Beanpublic UserDetailsService userDetailsService() {UserDetails userDetails = User.withDefaultPasswordEncoder().username("user").password("password").roles("USER", "user_info", "openid", "profile", "client.create").build();return new InMemoryUserDetailsManager(userDetails);}@Beanpublic 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);}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;}@Beanpublic JwtDecoder jwtDecoder(JWKSource<SecurityContext> jwkSource) {return OAuth2AuthorizationServerConfiguration.jwtDecoder(jwkSource);}@Beanpublic AuthorizationServerSettings authorizationServerSettings() {return AuthorizationServerSettings.builder().build();}
}

maven pom.xml的dependencies:

<dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-security</artifactId><version>3.2.2</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-oauth2-authorization-server</artifactId><version>3.2.2</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-configuration-processor</artifactId><version>3.2.2</version><optional>true</optional></dependency>
</dependencies>

一切配置完,然后启动程序。

授权码获取

访问授权码获取地址:

http://127.0.0.1:6001/oauth2/authorize?response_type=code&client_id=my-app&scope=user_info%20openid%20client.create&code_challenge=E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM&code_challenge_method=S256&redirect_uri=http://127.0.0.1:600/oauth2/code

想使用公共客户端认证方式,code_challenge和code_challenge_method是必不可少的参数。

1、在浏览器上输入上面的地址,然后请求会自动跳转到登录页面,输入用户名(user)和密码(password)进行登录。
在这里插入图片描述
2、登录成功后,会自动跳转到授权同意页面,选中对应的权限进行提交。
在这里插入图片描述
3、提交成功后,跳转到对应redirect_uri的地址(对应第1步url中的redirect_uri参数),授权码对应链接中的code值
在这里插入图片描述

http://127.0.0.1:600/oauth2/code?code=UZtsq9o-_6jRdT1C8xrV62dkynoFQ1aBSZtWzhFkoRAzPD-SpQ8UDppbY3VquKUQjFd5niMqzROMnnZQ_IdBpyEGUJ6qkvVuMYJ81M3zqwJodx6f5OerkgcQw4989Vvq

Token的获取

postman请求,使用POST方式,访问获取token地址:http://127.0.0.1:6001/oauth2/token
在这里插入图片描述
上面的参数都是必传的,code的值是授权码。
返回token结果:
在这里插入图片描述
在这里插入图片描述
到这里公共客户端授权码和token的获取到此结束。

如何验证code_verifier的有效性

在请求获取token的时候CodeVerifierAuthenticator.java中的codeVerifierValid方法来验证提交的code_verifier的有效性,如下:

/*** codeVerifier 请求获取token接口提交的code_verifier参数* codeChallenge 授权服务存储的code_challenge* codeChallengeMethod 授权服务存储的code_challenge_method=S256**/
private boolean codeVerifierValid(String codeVerifier, String codeChallenge, String codeChallengeMethod) {if (!StringUtils.hasText(codeVerifier)) {return false;} else if ("S256".equals(codeChallengeMethod)) {try {MessageDigest md = MessageDigest.getInstance("SHA-256");byte[] digest = md.digest(codeVerifier.getBytes(StandardCharsets.US_ASCII));String encodedVerifier = Base64.getUrlEncoder().withoutPadding().encodeToString(digest);return encodedVerifier.equals(codeChallenge);} catch (NoSuchAlgorithmException ex) {// It is unlikely that SHA-256 is not available on the server. If it is not available,// there will likely be bigger issues as well. We default to SERVER_ERROR.throw new OAuth2AuthenticationException(OAuth2ErrorCodes.SERVER_ERROR);}}return false;
}

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

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

相关文章

springboot149智慧图书管理系统设计与实现

智慧图书管理系统的设计与实现 摘 要 如今社会上各行各业&#xff0c;都在用属于自己专用的软件来进行工作&#xff0c;互联网发展到这个时候&#xff0c;人们已经发现离不开了互联网。互联网的发展&#xff0c;离不开一些新的技术&#xff0c;而新技术的产生往往是为了解决现…

【PostgresSQL系列】 ltree简介及基于SpringBoot实现 ltree数据增删改查

本文将对PostgresSQL中的ltree进行相关概念介绍&#xff0c;并以示例代码讲解ltree数据增删改查功能的实现。 作者&#xff1a;后端小肥肠 目录 1.前言 2. 基础概念 2.1. ltree 2.2. lquery 2.3. ltxtquery 2.4. ltree函数及操作符 2.4.1. ltree函数 2.4.2. ltree操作符…

Pymysql将爬取到的信息存储到数据库中

爬取平台为电影天堂 获取到的数据仅为测试学习而用 爬取内容为电影名和电影的下载地址 创建表时需要建立三个字段即可 import urllib.request import re import pymysqldef film_exists(film_name, film_link):"""判断插入的数据是否已经存在""&qu…

Linux 路由配置与使用

概念 路由信息用于指导数据包从源地址查找到目的地址传输路径的信息&#xff1b; 路由分类 根据路由信息的来源分为静态路由和动态路由 静态路由 由管理员手动配置的路由表项信息&#xff0c;根据路由形式的不同&#xff0c;静态路由又可细分为&#xff1a; 直连路由&#xf…

Vue3+TS+Vite+Pinia学习总结

VUE3介绍 vue2和vue3之间的区别 因为需要遍历data对象上所有属性&#xff0c;所以如果data对象属性结构嵌套很深&#xff0c;就会存在性能问题。因为需要遍历属性&#xff0c;所有需要提前知道对象上有哪些属性&#xff0c;才能将其转化为getter和setter,所以vue2中无法将data新…

Linux部署lomp环境,安装typecho、WordPress博客

部署lomp环境&#xff0c;安装typecho、WordPress博客 一、环境要求1.1.版本信息1.2.准备阿里云服务器【新用户免费使用三个月】1.3.准备远程工具【FinalShell】 二、Linux下安装openresty三、Linux下安装Mysql四、安装Apache【此步骤可省略】4.1.安装Apache服务及其扩展包4.2.…

Git―基本操作

Git ⛅认识 Git⛅安装 GitCentos(7.6)Ubuntu ⛅Git―基本操作创建本地仓库&#x1f342;配置本地仓库&#x1f342;工作区, 暂存区, 版本库&#x1f342;版本库工作区 添加文件&#x1f342;查看文件&#x1f342;修改文件&#x1f342;版本回退&#x1f342;☃️案例 撤销修改…

2024年美赛 (D题ICM)| 湖流网络水位控制 |数学建模完整代码+建模过程全解全析

当大家面临着复杂的数学建模问题时&#xff0c;你是否曾经感到茫然无措&#xff1f;作为2022年美国大学生数学建模比赛的O奖得主&#xff0c;我为大家提供了一套优秀的解题思路&#xff0c;让你轻松应对各种难题。 让我们来看看美赛的D题&#xff01; 完整内容可以在文章末尾领…

SpringBoot security 安全认证(二)——登录拦截器

本节内容&#xff1a;实现登录拦截器&#xff0c;除了登录接口之外所有接口访问都要携带Token&#xff0c;并且对Token合法性进行验证&#xff0c;实现登录状态的保持。 核心内容&#xff1a; 1、要实现登录拦截器&#xff0c;从Request请求中获取token&#xff0c;从缓存中获…

STM32 1-5

目录 STM32简介 点亮PC13LED GPIO LED闪烁 LED流水灯 按键控制LED 光敏传感器控制蜂鸣器 OLED调试工具 OLED显示 EXTI外部中断 对射式红外传感器计次 旋转编码器计次 继续 STM32简介 点亮PC13LED main.c #include "stm32f10x.h" // D…

【前端模板】bootstrap5实现蓝色数码网站BigTech(电商适用,附带源码)

一、需求分析 数码电商网站是指专门销售数码产品的在线商城。它们提供了一个平台&#xff0c;供消费者浏览、选择和购买各种数码产品&#xff0c;如智能手机、电脑、相机、家电等。这些网站通常提供以下功能&#xff1a; 产品展示&#xff1a;数码电商网站展示各种数码产品的详…

从零开始:构建高效的 JMeter 集群压测环境

当面对大量用户模拟和性能测量需求时&#xff0c;单台计算机运行 JMeter 往往显得力不从心。因此&#xff0c;构建一个多节点的JMeter集群成为了一种提升测试性能的有效途径。接下来&#xff0c;本文将详细介绍如何组建和配置一个JMeter测试集群。 一、准备工作&#xff1a;服…