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

篇二:springboot2.7 OAuth2 server使用jdbc存储RegisteredClient

上一篇 <<springboot 2.7 oauth server配置源码走读一>>中简单描述了oauth2 server的配置,其中使用了内存保存 RegisteredClient,本篇改用mysql存储。

db存储需要创建表,表结构应该是什么样的呢,从spring给我们封装好的源码入手,
org.springframework.security.oauth2.server.authorization.client.JdbcRegisteredClientRepository类中:
在这里插入图片描述
那么字段类型呢?我们看org.springframework.security.oauth2.server.authorization.client.RegisteredClient类:
在这里插入图片描述
解释下为什么时间用timestamp存储以及Set数据模型用字符串存储:
在这里插入图片描述
解释下为什么ClientSettings和TokenSettings用json存储(当然varchar也行):就是一个map.
在这里插入图片描述
在这里插入图片描述
至此咱们确定了表结构,如下是一个示例:


CREATE TABLE `oauth2_registered_client` (`id` varchar(36) COLLATE utf8mb4_general_ci NOT NULL,`client_id` varchar(100) COLLATE utf8mb4_general_ci NOT NULL,`client_id_issued_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,`client_secret` varchar(100) COLLATE utf8mb4_general_ci DEFAULT '',`client_secret_expires_at` timestamp NULL DEFAULT NULL,`client_name` varchar(50) COLLATE utf8mb4_general_ci NOT NULL,`client_authentication_methods` varchar(100) COLLATE utf8mb4_general_ci NOT NULL,`authorization_grant_types` varchar(100) COLLATE utf8mb4_general_ci NOT NULL,`redirect_uris` varchar(100) COLLATE utf8mb4_general_ci DEFAULT '',`scopes` varchar(200) COLLATE utf8mb4_general_ci NOT NULL,`client_settings` json NOT NULL,`token_settings` json NOT NULL,PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;

创建好表后,咱们处理代码:

1.在pom中引入依赖:

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.26</version>
</dependency>

2.yaml/properties配置文件中添加数据库信息,示例如下:

spring:datasource:url: jdbc:mysql://localhost:3306/db?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai&useSSL=falseusername: <username>password: <password>

3.使用JdbcRegisteredClientRepository,它和InMemoryRegisteredClientRepository只能二选一,所以需要注释掉后者。在咱们自己的配置类OAuth2AuthorizeSecurityConfig中:

@Beanpublic RegisteredClientRepository registeredClientRepository(JdbcTemplate jdbcTemplate) {return new JdbcRegisteredClientRepository(jdbcTemplate);}

4.初始化数据到db表中:写一个测试类方法插入数据:

package com.jel.tech.auth;import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
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.server.authorization.client.RegisteredClient;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;
import org.springframework.security.oauth2.server.authorization.config.ClientSettings;
import org.springframework.security.oauth2.server.authorization.config.TokenSettings;import javax.annotation.Resource;
import java.time.Duration;
import java.util.UUID;@SpringBootTest
class AuthApplicationTests {@Resourceprivate RegisteredClientRepository registeredClientRepository;@Testvoid saveRegisteredClients() {RegisteredClient loginClient = RegisteredClient.withId(UUID.randomUUID().toString()).clientId("login-client").clientSecret("{noop}openid-connect").clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC).authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE).authorizationGrantType(AuthorizationGrantType.REFRESH_TOKEN).redirectUri("http://127.0.0.1:8080/login/oauth2/code/login-client").redirectUri("http://127.0.0.1:8080/authorized").scope(OidcScopes.OPENID).scope(OidcScopes.PROFILE).clientSettings(ClientSettings.builder().requireAuthorizationConsent(true).build()).build();RegisteredClient registeredClient = RegisteredClient.withId(UUID.randomUUID().toString()).clientId("messaging-client").clientSecret("{noop}secret").clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC).authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS).scope("message:read").scope("message:write")// 指定token有效期:token:30分(默认5分钟),refresh_token:1天.tokenSettings(TokenSettings.builder().accessTokenTimeToLive(Duration.ofMinutes(30)).refreshTokenTimeToLive(Duration.ofDays(1)).build()).build();// 注意:没有设置clientName,则会把id值作为clientNameregisteredClientRepository.save(loginClient);registeredClientRepository.save(registeredClient);}
}

5.验证功能,在此我借花献佛,把官网提供的示例复制过来:


package com.jel.tech.auth;import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.HttpHeaders;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.request.RequestPostProcessor;
import org.springframework.test.web.servlet.result.MockMvcResultHandlers;import java.util.Map;import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;/*** Integration tests for {@link AuthApplication}.** @author Steve Riesenberg*/
@SpringBootTest
@AutoConfigureMockMvc
@ActiveProfiles("test")
public class OAuth2AuthorizationServerApplicationITests {private static final String CLIENT_ID = "messaging-client";private static final String CLIENT_SECRET = "secret";private final ObjectMapper objectMapper = new ObjectMapper();@Autowiredprivate MockMvc mockMvc;@Testvoid performTokenRequestWhenValidClientCredentialsThenOk() throws Exception {// @formatter:offthis.mockMvc.perform(post("/oauth2/token").param("grant_type", "client_credentials").param("scope", "message:read").with(basicAuth(CLIENT_ID, CLIENT_SECRET))).andExpect(status().isOk()).andExpect(jsonPath("$.access_token").isString()).andExpect(jsonPath("$.expires_in").isNumber()).andExpect(jsonPath("$.scope").value("message:read")).andExpect(jsonPath("$.token_type").value("Bearer"));// @formatter:on}@Testvoid performTokenRequestWhenMissingScopeThenOk() throws Exception {// @formatter:offthis.mockMvc.perform(post("/oauth2/token").param("grant_type", "client_credentials").with(basicAuth(CLIENT_ID, CLIENT_SECRET))).andExpect(status().isOk()).andExpect(jsonPath("$.access_token").isString()).andExpect(jsonPath("$.expires_in").isNumber()).andExpect(jsonPath("$.scope").value("message:read message:write")).andExpect(jsonPath("$.token_type").value("Bearer"));// @formatter:on}@Testvoid performTokenRequestWhenInvalidClientCredentialsThenUnauthorized() throws Exception {// @formatter:offthis.mockMvc.perform(post("/oauth2/token").param("grant_type", "client_credentials").param("scope", "message:read").with(basicAuth("bad", "password"))).andExpect(status().isUnauthorized()).andExpect(jsonPath("$.error").value("invalid_client"));// @formatter:on}@Testvoid performTokenRequestWhenMissingGrantTypeThenUnauthorized() throws Exception {// @formatter:offthis.mockMvc.perform(post("/oauth2/token").with(basicAuth("bad", "password"))).andExpect(status().isUnauthorized()).andExpect(jsonPath("$.error").value("invalid_client"));// @formatter:on}@Testvoid performTokenRequestWhenGrantTypeNotRegisteredThenBadRequest() throws Exception {// @formatter:offthis.mockMvc.perform(post("/oauth2/token").param("grant_type", "client_credentials").with(basicAuth("login-client", "openid-connect"))).andExpect(status().isBadRequest()).andExpect(jsonPath("$.error").value("unauthorized_client"));// @formatter:on}@Testvoid performIntrospectionRequestWhenValidTokenThenOk() throws Exception {// @formatter:offthis.mockMvc.perform(post("/oauth2/introspect").param("token", getAccessToken()).with(basicAuth(CLIENT_ID, CLIENT_SECRET))).andExpect(status().isOk()).andExpect(jsonPath("$.active").value("true")).andExpect(jsonPath("$.aud[0]").value(CLIENT_ID)).andExpect(jsonPath("$.client_id").value(CLIENT_ID)).andExpect(jsonPath("$.exp").isNumber()).andExpect(jsonPath("$.iat").isNumber()).andExpect(jsonPath("$.iss").value("http://127.0.0.1:9000")).andExpect(jsonPath("$.nbf").isNumber()).andExpect(jsonPath("$.scope").value("message:read")).andExpect(jsonPath("$.sub").value(CLIENT_ID)).andExpect(jsonPath("$.token_type").value("Bearer")).andDo(MockMvcResultHandlers.print());// @formatter:on}@Testvoid performIntrospectionRequestWhenInvalidCredentialsThenUnauthorized() throws Exception {// @formatter:offthis.mockMvc.perform(post("/oauth2/introspect").param("token", getAccessToken()).with(basicAuth("bad", "password"))).andExpect(status().isUnauthorized()).andExpect(jsonPath("$.error").value("invalid_client"));// @formatter:on}private String getAccessToken() throws Exception {// @formatter:offMvcResult mvcResult = this.mockMvc.perform(post("/oauth2/token").param("grant_type", "client_credentials").param("scope", "message:read").with(basicAuth(CLIENT_ID, CLIENT_SECRET))).andExpect(status().isOk()).andExpect(jsonPath("$.access_token").exists()).andReturn();// @formatter:onString tokenResponseJson = mvcResult.getResponse().getContentAsString();Map<String, Object> tokenResponse = this.objectMapper.readValue(tokenResponseJson, new TypeReference<Map<String, Object>>() {});String access_token = tokenResponse.get("access_token").toString();System.out.println(access_token);return access_token;}private static BasicAuthenticationRequestPostProcessor basicAuth(String username, String password) {return new BasicAuthenticationRequestPostProcessor(username, password);}private static final class BasicAuthenticationRequestPostProcessor implements RequestPostProcessor {private final String username;private final String password;private BasicAuthenticationRequestPostProcessor(String username, String password) {this.username = username;this.password = password;}@Overridepublic MockHttpServletRequest postProcessRequest(MockHttpServletRequest request) {HttpHeaders headers = new HttpHeaders();headers.setBasicAuth(this.username, this.password);request.addHeader("Authorization", headers.getFirst("Authorization"));return request;}}
}

相关文章:

篇二:springboot2.7 OAuth2 server使用jdbc存储RegisteredClient

上一篇 <<springboot 2.7 oauth server配置源码走读一>>中简单描述了oauth2 server的配置&#xff0c;其中使用了内存保存 RegisteredClient&#xff0c;本篇改用mysql存储。 db存储需要创建表&#xff0c;表结构应该是什么样的呢&#xff0c;从spring给我们封装好…...

卷积神经网络|导入图片

在学习卷积神经网络时&#xff0c;我们通常使用的就是公开的数据集&#xff0c;这里&#xff0c;我们不使用公开数据集&#xff0c;直接导入自己的图片数据&#xff0c;下面&#xff0c;就简单写个程序实现批量图片的导入。 import osfrom PIL import Imageimport numpy as np…...

关于unity的组件VerticalLayoutGroup刷新显示不正常的问题

先说明一下我是如何用到&#xff0c;有哪些处理的 用到这个组件基本上都是将列表进行排版操作的&#xff0c;竖着&#xff0c;或者横着&#xff0c;横着用HorizontalLayoutGroup 还有一个和这个组件搭配的组件叫ContentSizeFitter 先说我是怎么发现这个组件不好用的 //本地读取…...

wait 和 notify 这个为什么要在synchronized 代码块中?

一个工作七年的小伙伴&#xff0c;竟然不知道” wait”和“notify”为什么要在 Synchronized 代码块中 。 好吧&#xff0c;如果屏幕前的你也不知道&#xff0c;请在公屏上刷”不知道“。 对于这个问题&#xff0c;我们来看看普通人和高手的回答。 一、问题解析 1. wait 和 n…...

大白话说区块链和通证

1 区块链 简单地说&#xff0c;区块链其实就像是一个不可篡改的分布式数据库&#xff0c;该分布式数据库记录了一系列交易或事件。区块链运行在至少1个以上的节点上&#xff0c;每个节点都有自己的一个分布式数据库&#xff0c;也就是分布式账本。正常情况下&#xff0c;每个节…...

Jvm之垃圾收集器(个人见解仅供参考)

问&#xff1a;什么是垃圾收集算法中的分代收集理论&#xff1f; 答&#xff1a;分代收集理论是垃圾收集算法的一种思想&#xff0c;根据对象存活周期的不同将内存分为几块&#xff0c;一般将java堆分为新生代和老年代。这种理论使得我们可以根据各个年代的特点选择合适的垃圾收…...

Minitab 21软件安装包下载及安装教程

Minitab 21下载链接&#xff1a;https://docs.qq.com/doc/DUkNHZVhwTXhtTFla 1.选中下载好的安装包&#xff0c;鼠标右键解压到”Minitab 21“文件夹 2.选中”Setup.exe“&#xff0c;鼠标右击选择“以管理员身份运行” 3.点击“下一步” 4.点击“是” 5.点击“下一步” 6.勾选…...

Java版商城:Spring Cloud+SpringBoot b2b2c电子商务平台,多商家入驻、直播带货及免 费 小程序商城搭建

​随着互联网的快速发展&#xff0c;越来越多的企业开始注重数字化转型&#xff0c;以提升自身的竞争力和运营效率。在这个背景下&#xff0c;鸿鹄云商SAAS云产品应运而生&#xff0c;为企业提供了一种简单、高效、安全的数字化解决方案。 鸿鹄云商SAAS云产品是一种基于云计算的…...

阿里云被拉入黑洞模式怎么办?该怎么换ip-速盾网络

被拉入黑洞模式&#xff08;BGP黑洞路由&#xff09;意味着所有进入目标IP的流量都会被丢弃&#xff0c;从而导致目标IP对外完全不可访问。这种情况通常发生在面对大规模DDoS攻击时&#xff0c;为了防止攻击流量对其他网络造成影响。如果你使用的是阿里云服务并遭受到这种攻击&…...

Android 13.0 recovery竖屏界面旋转为横屏

1.概述 在13.0系统项目定制化开发中,由于平板固定横屏显示,而如果recovery界面竖屏显示就觉得怪怪的,所以需要recovery页面横屏显示的功能, 所以今天就来解决这个问题 2.实现功能相关分析 Android的Recovery中,利用 bootable\recovery下的minui库作为基础,采用的是直接…...

异地环控设备如何远程维护?贝锐蒲公英解决远程互联难题

青岛某企业致力于孵化设备、养禽设备和养猪设备的研发、生产和服务&#xff0c;历经三十多年发展&#xff0c;目前已成长为行业主要的养殖装备及工程服务提供商&#xff0c;产品覆盖养殖产业链中绝大多数环节&#xff0c;涉及自动化设备、环控设备、整体解决方案等。 在实际应用…...

flutter 判断是否是web环境

代码如下 import package:flutter/foundation.dart show kIsWeb;void main() {if (kIsWeb) {print(Running on the web!);} else {print(Not running on the web!);} } 如果是使用 Platform.isAndroid 会报错 所以使用上面的方式...

视频智能分析/云存储平台EasyCVR接入海康SDK,通道名称未自动更新该如何解决?

视频监控GB28181平台EasyCVR能在复杂的网络环境中&#xff0c;将分散的各类视频资源进行统一汇聚、整合、集中管理&#xff0c;在视频监控播放上&#xff0c;TSINGSEE青犀视频安防监控汇聚平台可支持1、4、9、16个画面窗口播放&#xff0c;可同时播放多路视频流&#xff0c;也能…...

后端开发——JDBC的学习(三)

本篇继续对JDBC进行总结&#xff1a; ①通过Service层与Dao层实现转账的练习&#xff1b; ②重点&#xff1a;由于每次使用连接就手动创建连接&#xff0c;用完后就销毁&#xff0c;这样会导致资源浪费&#xff0c;因此引入连接池&#xff0c;练习连接池的使用&#xff1b; …...

Redis 生产环境查找无过期时间的 key

在项目中,Redis 不应该被当作传统数据库来使用;储存大量没有过期时间的数据。如果储存大量无过期时间,而且无效的key的话;再加上 Redis 本身的过期策略没有被正确设置,就会大量占用内存。这样就会导致再多的内存资源也不够用。 情况大致是这样,项目中采用 Redis 二级存储…...

Visual Studio 2017编译Python3.8.18源码

一直纠结Python的开发环境没有升级到最新版3.8.18。这是当前的最新版&#xff0c;正在用的版本3.8.10。他是官方制作出安装包的最新版。 1、准备Visual C 2017的开发环境包括但不限于使用C的桌面开发x64 2、在Python官网下载Python3.8.18的源码。 3、解压缩源码 4、进入控制…...

【mujoco】Ubuntu20.04中解决mujoco报错raise error.MujocoDependencyError

【mujoco】Ubuntu20.04中解决mujoco报错raise error.MujocoDependencyError 文章目录 【mujoco】Ubuntu20.04中解决mujoco报错raise error.MujocoDependencyError1. 报错的具体情况2. 解决过程3. 其他问题3.1 ModuleNotFoundError: No module named OpenGL3.2 ModuleNotFoundEr…...

机器学习的三个方面

1 机器学习的三个方面 1.1 数据 包括数据采集、增强和质量管理&#xff0c;相当于给人工智能模型学习什么样的知识 第一、什么专业的知识&#xff1b; 第二、知识是否有体系&#xff0c;也就是说样本之间是否存在某种关联、差异等&#xff0c;这个涉及到样本选择等问题&#x…...

关于一名资深Java程序员在移动端的进阶之路

今天呢&#xff0c;就借此机会&#xff0c;跟大家聊一聊我的个人职业经历吧&#xff01; 那年刚毕业 刚毕业时候&#xff0c;入职的第一家公司&#xff0c;进去后&#xff0c;说实话&#xff0c;没有太大成长吧&#xff01;基本就是让我做一些可有可无的边缘性的工作&#xff…...

clickonce excel 插件发布安装的原理

ClickOnce 是一种由 Microsoft 提供的部署技术&#xff0c;用于简化和加速Windows应用程序的部署。ClickOnce 可以用于部署各种类型的应用程序&#xff0c;包括 Excel 插件。 以下是 ClickOnce Excel 插件发布和安装的一般原理&#xff1a; 1. 发布应用程序&#xff1a; -…...

基于大模型的 UI 自动化系统

基于大模型的 UI 自动化系统 下面是一个完整的 Python 系统,利用大模型实现智能 UI 自动化,结合计算机视觉和自然语言处理技术,实现"看屏操作"的能力。 系统架构设计 #mermaid-svg-2gn2GRvh5WCP2ktF {font-family:"trebuchet ms",verdana,arial,sans-…...

论文解读:交大港大上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(二)

HoST框架核心实现方法详解 - 论文深度解读(第二部分) 《Learning Humanoid Standing-up Control across Diverse Postures》 系列文章: 论文深度解读 + 算法与代码分析(二) 作者机构: 上海AI Lab, 上海交通大学, 香港大学, 浙江大学, 香港中文大学 论文主题: 人形机器人…...

中南大学无人机智能体的全面评估!BEDI:用于评估无人机上具身智能体的综合性基准测试

作者&#xff1a;Mingning Guo, Mengwei Wu, Jiarun He, Shaoxian Li, Haifeng Li, Chao Tao单位&#xff1a;中南大学地球科学与信息物理学院论文标题&#xff1a;BEDI: A Comprehensive Benchmark for Evaluating Embodied Agents on UAVs论文链接&#xff1a;https://arxiv.…...

理解 MCP 工作流:使用 Ollama 和 LangChain 构建本地 MCP 客户端

&#x1f31f; 什么是 MCP&#xff1f; 模型控制协议 (MCP) 是一种创新的协议&#xff0c;旨在无缝连接 AI 模型与应用程序。 MCP 是一个开源协议&#xff0c;它标准化了我们的 LLM 应用程序连接所需工具和数据源并与之协作的方式。 可以把它想象成你的 AI 模型 和想要使用它…...

对WWDC 2025 Keynote 内容的预测

借助我们以往对苹果公司发展路径的深入研究经验&#xff0c;以及大语言模型的分析能力&#xff0c;我们系统梳理了多年来苹果 WWDC 主题演讲的规律。在 WWDC 2025 即将揭幕之际&#xff0c;我们让 ChatGPT 对今年的 Keynote 内容进行了一个初步预测&#xff0c;聊作存档。等到明…...

Java多线程实现之Callable接口深度解析

Java多线程实现之Callable接口深度解析 一、Callable接口概述1.1 接口定义1.2 与Runnable接口的对比1.3 Future接口与FutureTask类 二、Callable接口的基本使用方法2.1 传统方式实现Callable接口2.2 使用Lambda表达式简化Callable实现2.3 使用FutureTask类执行Callable任务 三、…...

【单片机期末】单片机系统设计

主要内容&#xff1a;系统状态机&#xff0c;系统时基&#xff0c;系统需求分析&#xff0c;系统构建&#xff0c;系统状态流图 一、题目要求 二、绘制系统状态流图 题目&#xff1a;根据上述描述绘制系统状态流图&#xff0c;注明状态转移条件及方向。 三、利用定时器产生时…...

Rust 开发环境搭建

环境搭建 1、开发工具RustRover 或者vs code 2、Cygwin64 安装 https://cygwin.com/install.html 在工具终端执行&#xff1a; rustup toolchain install stable-x86_64-pc-windows-gnu rustup default stable-x86_64-pc-windows-gnu ​ 2、Hello World fn main() { println…...

Chrome 浏览器前端与客户端双向通信实战

Chrome 前端&#xff08;即页面 JS / Web UI&#xff09;与客户端&#xff08;C 后端&#xff09;的交互机制&#xff0c;是 Chromium 架构中非常核心的一环。下面我将按常见场景&#xff0c;从通道、流程、技术栈几个角度做一套完整的分析&#xff0c;特别适合你这种在分析和改…...

五子棋测试用例

一.项目背景 1.1 项目简介 传统棋类文化的推广 五子棋是一种古老的棋类游戏&#xff0c;有着深厚的文化底蕴。通过将五子棋制作成网页游戏&#xff0c;可以让更多的人了解和接触到这一传统棋类文化。无论是国内还是国外的玩家&#xff0c;都可以通过网页五子棋感受到东方棋类…...