文章目录
- 写在前面的话
- 1. 集成环境
- 2. 了解OAuth2.1和Spring Authorization Server
-
- 2.1 OAuth2.1
- 2.2 spring-security-oauth2-authorization-server
- 3. 项目搭建
-
- 3.1 认证服务器框架搭建
- 3.2 初始化自带的数据表
- 3.3 验证核心配置AuthorizationServerConfig
-
- 3.3.1 用于协议端点的Spring Security过滤器链
- 3.3.2 用于认证的Spring Security过滤器链
- 3.3.3 UserDetailsService 的一个实例,用于检索要认证的用户。
- 3.3.4 RegisteredClientRepository 的一个实例,用于管理客户端
- 3.3.5 配置解析JWT access-token的解析器
- 3.3.6 AuthorizationServerSettings的一个实例,用于配置Spring授权服务器
- 3.3.7 配置数据源信息
- 3.4 启动项目
-
- 3.4.1 访问/oauth2/authorize前往登录页面
- 3.4.2 登录成功后跳转授权页面
写在前面的话
因为SpringBoot3.x是目前最新的版本,整合spring-security-oauth2-authorization-server的资料很少,所以产生了这篇文章,主要为想尝试SpringBoot高版本,想整合最新的spring-security-oauth2-authorization-server的初学者,旨在为大家提供一个简单上手的参考,如果哪里写得不对或可以优化的还请大家踊跃评论指正。
1. 集成环境
JDK-17
spring-boot-dependencies-3.1.3
spring-security-oauth2-authorization-server-1.1.2
此文章主要参照官方文档编写,所有配置都能在官方文档上找到。
https://docs.spring.io/spring-authorization-server/docs/current/reference/html/getting-started.html
2. 了解OAuth2.1和Spring Authorization Server
2.1 OAuth2.1
关于OAuth2.1的介绍和规范可以参考官方文档:https://datatracker.ietf.org/doc/html/rfc6749#section-1
2.2 spring-security-oauth2-authorization-server
概述取自官方文档:
Spring授权服务器是一个框架,提供 OAuth 2.1 和 OpenID Connect 1.0 规范及其他相关规范的实现。它建立在 Spring Security 之上,为构建OpenID Connect 1.0 Identity Provider 和OAuth2 授权服务器产品提供了一个安全、轻量级和可定制的基础。
3. 项目搭建
3.1 认证服务器框架搭建
pom.xml
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.0modelVersion>
parent>
groupId>org.springframework.bootgroupId>
artifactId>spring-boot-starter-parentartifactId>
version>3.1.3version>
relativePath/>
parent>
groupId>com.roshinegroupId>
artifactId>authorization-serverartifactId>
version>0.0.1-SNAPSHOTversion>
name>authorization-servername>
description>authorization-serverdescription>
properties>
java.version>17java.version>
properties>
dependencies>
dependency>
groupId>org.springframework.bootgroupId>
artifactId>spring-boot-starter-oauth2-authorization-serverartifactId>
dependency>
dependency>
groupId>mysqlgroupId>
artifactId>mysql-connector-javaartifactId>
version>8.0.28version>
dependency>
dependency>
groupId>com.alibabagroupId>
artifactId>druid-spring-boot-starterartifactId>
version>1.2.19version>
dependency>
dependency>
groupId>com.baomidougroupId>
artifactId>mybatis-plus-boot-starterartifactId>
version>3.5.3.2version>
dependency>
dependency>
groupId>org.projectlombokgroupId>
artifactId>lombokartifactId>
optional>trueoptional>
dependency>
dependency>
groupId>org.springframework.bootgroupId>
artifactId>spring-boot-starter-testartifactId>
scope>testscope>
dependency>
dependencies>
build>
plugins>
plugin>
groupId>org.springframework.bootgroupId>
artifactId>spring-boot-maven-pluginartifactId>
configuration>
excludes>
exclude>
groupId>org.projectlombokgroupId>
artifactId>lombokartifactId>
exclude>
excludes>
configuration>
plugin>
plugins>
build>
project>
3.2 初始化自带的数据表
- 已注册的客户端信息表
- 认证授权表
- 认证信息表
3.3 验证核心配置AuthorizationServerConfig
改配置完全参照官方文档,内容如下
3.3.1 用于协议端点的Spring Security过滤器链
此处与官方文档略有不同:
- 注释 .oidc(Customizer.withDefaults()),因为不清楚如何使用和验证,注释掉先
- 默认前往登录页的uri是/login,如果需要自定义登录页可以改为自己的,这里我就暂时先用默认
/**
* 用于协议端点的Spring Security过滤器链
*
* @param http HttpSecurity
* @return SecurityFilterChain
*/
@Bean
public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {
OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(http);
http.getConfigurer(OAuth2AuthorizationServerConfigurer.class);
// 开启OpenID Connect 1.0 暂时不清楚该协议是什么,那就不用先
//.oidc(Customizer.withDefaults());
http
// 当未登录时访问认证端点时重定向至登录页面,默认前往登录页的uri是/login
.exceptionHandling((exceptions) -> exceptions
.authenticationEntryPoint(new LoginUrlAuthenticationEntryPoint("/login")));
return http.build();
}
3.3.2 用于认证的Spring Security过滤器链
/**
* 用于认证的Spring Security过滤器链。
*
* @param http HttpSecurity
* @return SecurityFilterChain
*/
@Bean
public SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {
http.authorizeHttpRequests((authorize) -> authorize
.anyRequest().authenticated())
.formLogin(Customizer.withDefaults());
return http.build();
}
3.3.3 UserDetailsService 的一个实例,用于检索要认证的用户。
/**
* 配置密码解析器,使用BCrypt的方式对密码进行加密和验证
*
* @return BCryptPasswordEncoder
*/
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
public UserDetailsService userDetailsService() {
UserDetails userDetails = User.withUsername("admin")
.password(passwordEncoder().encode("123456"))
.roles("admin")
.build();
return new InMemoryUserDetailsManager(userDetails);
}
3.3.4 RegisteredClientRepository 的一个实例,用于管理客户端
如果客户端信息已经存在于数据库则使用,否则存入
/**
* RegisteredClientRepository 的一个实例,用于管理客户端
*
* @param jdbcTemplate jdbcTemplate
* @param passwordEncoder passwordEncoder
* @return RegisteredClientRepository
*/
@Bean
public RegisteredClientRepository registeredClientRepository(JdbcTemplate jdbcTemplate, PasswordEncoder passwordEncoder) {
RegisteredClient registeredClient = RegisteredClient.withId(UUID.randomUUID().toString())
.clientId("oauth2-client")
.clientSecret(passwordEncoder.encode("123456"))
// 客户端认证基于请求头
.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)
// 配置授权的支持方式
.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
.authorizationGrantType(AuthorizationGrantType.REFRESH_TOKEN)
.authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS)
.redirectUri("https://www.baidu.com")
.scope("user")
.scope("admin")
// 客户端设置,设置用户需要确认授权
.clientSettings(ClientSettings.builder().requireAuthorizationConsent(true).build())
.build();
JdbcRegisteredClientRepository registeredClientRepository = new JdbcRegisteredClientRepository(jdbcTemplate);
RegisteredClient repositoryByClientId = registeredClientRepository.findByClientId(registeredClient.getClientId());
if (repositoryByClientId == null) {
registeredClientRepository.save(registeredClient);
}
return registeredClientRepository;
}
3.3.5 配置解析JWT access-token的解析器
因为spring-security-oauth2-authorization-server默认使用JWT作为access-token,所以需要添加此配置
/**
* com.nimbusds.jose.jwk.source.JWKSource 的一个实例,用于签署访问令牌(access token)
*
* @return JWKSource
*/
@Bean
public JWKSourceSecurityContext> 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);
}
/**
* java.security.KeyPair 的一个实例,其 key 在启动时生成,用于创建上述 JWKSource。
*
* @return KeyPair
*/
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;
}
/**
* JwtDecoder 的一个实例,用于解码签名访问令牌(access token)
*
* @param jwkSource jwkSource
* @return JwtDecoder
*/
@Bean
public JwtDecoder jwtDecoder(JWKSourceSecurityContext> jwkSource) {
return OAuth2AuthorizationServerConfiguration.jwtDecoder(jwkSource);
}
3.3.6 AuthorizationServerSettings的一个实例,用于配置Spring授权服务器
此处还是走默认配置
@Bean
public AuthorizationServerSettings authorizationServerSettings() {
return AuthorizationServerSettings.builder().build();
}
3.3.7 配置数据源信息
server:
port: 8080
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
type: com.zaxxer.hikari.HikariDataSource
url: jdbc:mysql://xxx:3306/adp_bmc?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=UTC
username: xxx
password: xxx
3.4 启动项目
会看到oauth2_registered_client表写入了我们硬性配置的客户端信息
3.4.1 访问/oauth2/authorize前往登录页面
http://127.0.0.1:8080/oauth2/authorize?client_id=oauth2-client&response_type=code&scope=user&redirect_uri=https://www.baidu.com
3.4.2 登录成功后跳转授权页面
授权完成后,跳转了我们的认证成功回调地址
获取到授权码我们就可以访问/oauth2/token获取JWT token了
这是一篇纯基础搭建的文章,主要参考自官方文档,官方文档才是新技术的根本。
下一篇文章《spring-security-oauth2-authorization-server(二)token生成策略分析》会结合部分源码简要分析token几种生成策略。