本文目录导读:

我将为您提供一个完整的Spring Boot整合OAuth2的案例,包括授权服务器和资源服务器的完整实现。
项目结构
spring-boot-oauth2/
├── auth-server/ # 授权服务器
├── resource-server/ # 资源服务器
└── client/ # 客户端应用
授权服务器实现
创建授权服务器项目
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<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
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.14</version>
</parent>
<groupId>com.example</groupId>
<artifactId>auth-server</artifactId>
<version>1.0.0</version>
<name>auth-server</name>
<description>OAuth2 授权服务器</description>
<properties>
<java.version>11</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Spring Security -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!-- Spring Security OAuth2 -->
<dependency>
<groupId>org.springframework.security.oauth.boot</groupId>
<artifactId>spring-security-oauth2-autoconfigure</artifactId>
<version>2.6.8</version>
</dependency>
<!-- JWT支持 -->
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-jwt</artifactId>
<version>1.1.1.RELEASE</version>
</dependency>
<!-- 数据库 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<!-- 测试 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>
授权服务器主配置类
OAuth2AuthorizationServerConfig.java
package com.example.authserver.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.client.JdbcClientDetailsService;
import org.springframework.security.oauth2.provider.code.AuthorizationCodeServices;
import org.springframework.security.oauth2.provider.code.JdbcAuthorizationCodeServices;
import org.springframework.security.oauth2.provider.token.DefaultTokenServices;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JdbcTokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;
import javax.sql.DataSource;
@Configuration
@EnableAuthorizationServer
public class OAuth2AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
@Autowired
private AuthenticationManager authenticationManager;
@Autowired
private UserDetailsService userDetailsService;
@Autowired
private PasswordEncoder passwordEncoder;
@Autowired
private DataSource dataSource;
// 客户端信息服务
@Bean
@Primary
public ClientDetailsService clientDetailsService() {
return new JdbcClientDetailsService(dataSource);
}
// 令牌存储方式(使用JDBC存储或JWT)
// 选择1:JDBC存储
// @Bean
// public TokenStore tokenStore() {
// return new JdbcTokenStore(dataSource);
// }
// 选择2:JWT令牌
@Bean
public TokenStore tokenStore() {
return new JwtTokenStore(jwtAccessTokenConverter());
}
@Bean
public JwtAccessTokenConverter jwtAccessTokenConverter() {
JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
// 使用对称密钥,生产环境应使用非对称密钥
converter.setSigningKey("my-signing-key-123456789");
return converter;
}
// 授权码服务
@Bean
public AuthorizationCodeServices authorizationCodeServices() {
return new JdbcAuthorizationCodeServices(dataSource);
}
// 配置客户端信息
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.withClientDetails(clientDetailsService());
// 也可以使用内存方式
// clients.inMemory()
// .withClient("client")
// .secret(passwordEncoder.encode("secret"))
// .authorizedGrantTypes("authorization_code", "password", "refresh_token", "implicit")
// .scopes("read", "write")
// .redirectUris("http://localhost:8082/callback")
// .accessTokenValiditySeconds(3600)
// .refreshTokenValiditySeconds(86400);
}
// 配置令牌端点
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
endpoints
.authenticationManager(authenticationManager)
.userDetailsService(userDetailsService)
.tokenStore(tokenStore())
.tokenServices(tokenServices())
.authorizationCodeServices(authorizationCodeServices())
.accessTokenConverter(jwtAccessTokenConverter());
}
// 配置令牌服务
@Bean
@Primary
public DefaultTokenServices tokenServices() {
DefaultTokenServices tokenServices = new DefaultTokenServices();
tokenServices.setTokenStore(tokenStore());
tokenServices.setSupportRefreshToken(true);
tokenServices.setReuseRefreshToken(false);
tokenServices.setAccessTokenValiditySeconds(3600); // 1小时
tokenServices.setRefreshTokenValiditySeconds(86400); // 24小时
return tokenServices;
}
// 配置安全约束
@Override
public void configure(AuthorizationServerSecurityConfigurer security) {
// 允许客户端的表单认证
security
.tokenKeyAccess("permitAll()")
.checkTokenAccess("isAuthenticated()")
.allowFormAuthenticationForClients();
}
}
数据库初始化脚本
schema.sql
-- 创建客户端详情表
CREATE TABLE IF NOT EXISTS oauth_client_details (
client_id VARCHAR(256) PRIMARY KEY,
resource_ids VARCHAR(256),
client_secret VARCHAR(256),
scope VARCHAR(256),
authorized_grant_types VARCHAR(256),
web_server_redirect_uri VARCHAR(256),
authorities VARCHAR(256),
access_token_validity INTEGER,
refresh_token_validity INTEGER,
additional_information VARCHAR(4096),
autoapprove VARCHAR(256)
);
-- 创建令牌存储表(如果使用JDBC存储方式)
CREATE TABLE IF NOT EXISTS oauth_access_token (
token_id VARCHAR(256),
token BLOB,
authentication_id VARCHAR(256),
user_name VARCHAR(256),
client_id VARCHAR(256),
authentication BLOB,
refresh_token VARCHAR(256)
);
CREATE TABLE IF NOT EXISTS oauth_refresh_token (
token_id VARCHAR(256),
token BLOB,
authentication BLOB
);
-- 创建授权码表
CREATE TABLE IF NOT EXISTS oauth_code (
code VARCHAR(256),
authentication BLOB
);
-- 创建用户表
CREATE TABLE IF NOT EXISTS users (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(256) NOT NULL UNIQUE,
password VARCHAR(256) NOT NULL,
enabled BOOLEAN DEFAULT TRUE,
account_non_expired BOOLEAN DEFAULT TRUE,
account_non_locked BOOLEAN DEFAULT TRUE,
credentials_non_expired BOOLEAN DEFAULT TRUE
);
-- 创建角色表
CREATE TABLE IF NOT EXISTS authorities (
username VARCHAR(256) NOT NULL,
authority VARCHAR(256) NOT NULL,
PRIMARY KEY (username, authority),
FOREIGN KEY (username) REFERENCES users (username)
);
data.sql
-- 初始化客户端信息
INSERT INTO oauth_client_details (
client_id,
resource_ids,
client_secret,
scope,
authorized_grant_types,
web_server_redirect_uri,
authorities,
access_token_validity,
refresh_token_validity,
autoapprove
) VALUES (
'client',
'resource-server',
'$2a$10$XlW/e0pD5GXw4xW5X7M5YOuXyEnfIL5gQyGWwVKmXp1IcVpV/xqyO', -- 明文为: secret
'read,write',
'authorization_code,password,refresh_token,client_credentials,implicit',
'http://localhost:8082/callback',
'ROLE_CLIENT',
3600,
86400,
'true'
);
-- 初始化用户
INSERT INTO users (username, password, enabled) VALUES
('admin', '$2a$10$XlW/e0pD5GXw4xW5X7M5YOuXyEnfIL5gQyGWwVKmXp1IcVpV/xqyO', true); -- 密码: 123456
-- 添加角色
INSERT INTO authorities (username, authority) VALUES
('admin', 'ROLE_ADMIN'),
('admin', 'ROLE_USER');
用户详情服务
UserDetailsServiceImpl.java
package com.example.authserver.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.stream.Collectors;
@Service
public class UserDetailsServiceImpl implements UserDetailsService {
@Autowired
private JdbcTemplate jdbcTemplate;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
// 查询用户
List<String> user = jdbcTemplate.queryForList(
"SELECT * FROM users WHERE username = ?",
new Object[]{username},
String.class
);
if (user.isEmpty()) {
throw new UsernameNotFoundException("用户不存在");
}
// 查询用户角色
List<SimpleGrantedAuthority> authorities = jdbcTemplate.query(
"SELECT authority FROM authorities WHERE username = ?",
new Object[]{username},
(rs, rowNum) -> new SimpleGrantedAuthority(rs.getString("authority"))
);
return new User(username,
jdbcTemplate.queryForObject(
"SELECT password FROM users WHERE username = ?",
String.class,
username
),
authorities
);
}
}
安全配置
SecurityConfig.java
package com.example.authserver.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private UserDetailsService userDetailsService;
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService)
.passwordEncoder(passwordEncoder());
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests()
.antMatchers("/oauth/**", "/login", "/resources/**").permitAll()
.antMatchers("/actuator/**").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.permitAll()
.and()
.logout()
.permitAll();
}
}
应用配置
application.yml
server:
port: 8081
spring:
application:
name: auth-server
datasource:
url: jdbc:mysql://localhost:3306/oauth2_demo?useUnicode=true&characterEncoding=utf-8
username: root
password: root
driver-class-name: com.mysql.cj.jdbc.Driver
initialization-mode: always
schema: classpath:db/schema.sql
data: classpath:db/data.sql
sql:
init:
mode: always
schema-locations: classpath:db/schema.sql
data-locations: classpath:db/data.sql
jpa:
hibernate:
ddl-auto: none
show-sql: true
logging:
level:
org.springframework.security: INFO
org.springframework.web: INFO
资源服务器实现
资源服务器主配置
OAuth2ResourceServerConfig.java
package com.example.resourceserver.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.token.RemoteTokenServices;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
@Configuration
@EnableResourceServer
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class OAuth2ResourceServerConfig extends ResourceServerConfigurerAdapter {
@Value("${security.oauth2.client.client-id}")
private String clientId;
@Value("${security.oauth2.client.client-secret}")
private String clientSecret;
@Value("${security.oauth2.authorization.check-token-access}")
private String checkTokenEndpointUrl;
@Bean
public RemoteTokenServices tokenServices() {
RemoteTokenServices tokenServices = new RemoteTokenServices();
tokenServices.setClientId(clientId);
tokenServices.setClientSecret(clientSecret);
tokenServices.setCheckTokenEndpointUrl(checkTokenEndpointUrl);
return tokenServices;
}
@Override
public void configure(ResourceServerSecurityConfigurer resources) {
resources
.resourceId("resource-server")
.tokenServices(tokenServices());
}
@Override
public void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/public/**").permitAll()
.antMatchers("/api/admin/**").hasRole("ADMIN")
.antMatchers("/api/user/**").hasAnyRole("USER", "ADMIN")
.anyRequest().authenticated();
}
}
资源API控制器
UserController.java
package com.example.resourceserver.controller;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.bind.annotation.*;
import java.util.HashMap;
import java.util.Map;
@RestController
@RequestMapping("/api")
public class UserController {
@GetMapping("/public/message")
public Map<String, String> publicMessage() {
Map<String, String> response = new HashMap<>();
response.put("message", "这是公开信息");
return response;
}
@GetMapping("/user/info")
@PreAuthorize("hasAnyRole('USER', 'ADMIN')")
public Map<String, Object> getUserInfo(Authentication authentication) {
Map<String, Object> response = new HashMap<>();
response.put("username", authentication.getName());
response.put("authorities", authentication.getAuthorities());
return response;
}
@GetMapping("/admin/manage")
@PreAuthorize("hasRole('ADMIN')")
public Map<String, String> adminManage() {
Map<String, String> response = new HashMap<>();
response.put("message", "这是管理员才能看到的信息");
return response;
}
@GetMapping("/user/profile")
public Map<String, Object> getUserProfile() {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
Map<String, Object> response = new HashMap<>();
response.put("username", authentication.getName());
response.put("timestamp", System.currentTimeMillis());
return response;
}
}
资源服务器配置
application.yml
server:
port: 8082
spring:
application:
name: resource-server
security:
oauth2:
client:
client-id: client
client-secret: secret
authorization:
check-token-access: http://localhost:8081/oauth/check_token
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka/
logging:
level:
org.springframework.security: DEBUG
客户端应用实现
创建客户端应用
pom.xml
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Spring Security -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!-- OAuth2 Client -->
<dependency>
<groupId>org.springframework.security.oauth.boot</groupId>
<artifactId>spring-security-oauth2-autoconfigure</artifactId>
<version>2.6.8</version>
</dependency>
<!-- WebClient for calling resource server -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
</dependencies>
客户端控制器
ClientController.java
package com.example.client.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.*;
import org.springframework.security.oauth2.client.OAuth2RestTemplate;
import org.springframework.security.oauth2.client.token.grant.password.ResourceOwnerPasswordResourceDetails;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
@RestController
@RequestMapping("/client")
public class ClientController {
@Autowired
private OAuth2RestTemplate oAuth2RestTemplate;
@GetMapping("/login")
public Map<String, Object> login(
@RequestParam String username,
@RequestParam String password,
HttpSession session) {
// 使用密码模式获取令牌
ResourceOwnerPasswordResourceDetails resourceDetails =
new ResourceOwnerPasswordResourceDetails();
resourceDetails.setUsername(username);
resourceDetails.setPassword(password);
resourceDetails.setAccessTokenUri("http://localhost:8081/oauth/token");
resourceDetails.setClientId("client");
resourceDetails.setClientSecret("secret");
resourceDetails.setGrantType("password");
resourceDetails.setScope(Arrays.asList("read", "write"));
OAuth2RestTemplate oauth2RestTemplate = new OAuth2RestTemplate(resourceDetails);
OAuth2AccessToken accessToken = oauth2RestTemplate.getAccessToken();
// 保存令牌到session
session.setAttribute("accessToken", accessToken);
Map<String, Object> response = new HashMap<>();
response.put("success", true);
response.put("accessToken", accessToken.getValue());
response.put("expiresIn", accessToken.getExpiresIn());
response.put("refreshToken", accessToken.getRefreshToken() != null ?
accessToken.getRefreshToken().getValue() : null);
return response;
}
@GetMapping("/call-user-api")
public String callUserApi(HttpServletRequest request) {
try {
// 调用资源服务器
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
headers.setAuthorization("Bearer " +
((OAuth2AccessToken) request.getSession().getAttribute("accessToken")).getValue());
HttpEntity<String> entity = new HttpEntity<>(headers);
ResponseEntity<String> response = oAuth2RestTemplate.exchange(
"http://localhost:8082/api/user/info",
HttpMethod.GET,
entity,
String.class
);
return "资源服务器响应: " + response.getBody();
} catch (Exception e) {
return "调用失败: " + e.getMessage();
}
}
@GetMapping("/refresh-token")
public Map<String, Object> refreshToken(HttpServletRequest request) {
OAuth2AccessToken oldToken =
(OAuth2AccessToken) request.getSession().getAttribute("accessToken");
// 刷新令牌
OAuth2AccessToken newToken = oAuth2RestTemplate.refreshAccessToken();
request.getSession().setAttribute("accessToken", newToken);
Map<String, Object> response = new HashMap<>();
response.put("success", true);
response.put("newAccessToken", newToken.getValue());
return response;
}
}
客户端安全配置
SecurityConfig.java
package com.example.client.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.oauth2.client.OAuth2RestTemplate;
import org.springframework.security.oauth2.client.token.grant.client.ClientCredentialsResourceDetails;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableOAuth2Client;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import java.util.Arrays;
@Configuration
@EnableOAuth2Client
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Bean
public OAuth2RestTemplate oAuth2RestTemplate() {
ClientCredentialsResourceDetails resourceDetails =
new ClientCredentialsResourceDetails();
resourceDetails.setClientId("client");
resourceDetails.setClientSecret("secret");
resourceDetails.setAccessTokenUri("http://localhost:8081/oauth/token");
resourceDetails.setScope(Arrays.asList("read", "write"));
return new OAuth2RestTemplate(resourceDetails);
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests()
.antMatchers("/client/login", "/").permitAll()
.anyRequest().authenticated()
.and()
.httpBasic();
}
}
客户端配置
application.yml
server:
port: 8083
spring:
application:
name: oauth2-client
security:
oauth2:
client:
client-id: client
client-secret: secret
access-token-uri: http://localhost:8081/oauth/token
user-authorization-uri: http://localhost:8081/oauth/authorize
scope: read,write
resource:
user-info-uri: http://localhost:8082/api/user/info
测试用例
测试密码模式获取令牌
curl -X POST http://localhost:8081/oauth/token \ -H "Content-Type: application/x-www-form-urlencoded" \ -d "grant_type=password&username=admin&password=123456&client_id=client&client_secret=secret&scope=read write"
测试客户端模式获取令牌
curl -X POST http://localhost:8081/oauth/token \ -H "Content-Type: application/x-www-form-urlencoded" \ -d "grant_type=client_credentials&client_id=client&client_secret=secret&scope=read"
测试刷新令牌
curl -X POST http://localhost:8081/oauth/token \ -H "Content-Type: application/x-www-form-urlencoded" \ -d "grant_type=refresh_token&refresh_token=<REFRESH_TOKEN>&client_id=client&client_secret=secret"
测试访问资源服务器
curl http://localhost:8082/api/user/info \ -H "Authorization: Bearer <ACCESS_TOKEN>"
扩展功能
JWT增强配置
@Configuration
public class JwtConfig {
@Bean
public JwtAccessTokenConverter jwtAccessTokenConverter() {
JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
// 使用非对称加密
KeyPair keyPair = generateKeyPair();
converter.setKeyPair(keyPair);
return converter;
}
private KeyPair generateKeyPair() {
// 生成RSA密钥对
try {
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
keyPairGenerator.initialize(2048);
return keyPairGenerator.generateKeyPair();
} catch (Exception e) {
throw new RuntimeException("生成密钥对失败", e);
}
}
}
添加自定义Token增强
@Component
public class CustomTokenEnhancer implements TokenEnhancer {
@Override
public OAuth2AccessToken enhance(
OAuth2AccessToken accessToken,
OAuth2Authentication authentication) {
Map<String, Object> additionalInfo = new HashMap<>();
additionalInfo.put("organization", authentication.getName());
((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(additionalInfo);
return accessToken;
}
}
使用Docker部署
docker-compose.yml
version: '3'
services:
mysql:
image: mysql:8.0
container_name: oauth2-mysql
environment:
MYSQL_DATABASE: oauth2_demo
MYSQL_ROOT_PASSWORD: root
ports:
- "3306:3306"
volumes:
- ./mysql-data:/var/lib/mysql
auth-server:
build:
context: ./auth-server
container_name: oauth2-auth-server
ports:
- "8081:8081"
depends_on:
- mysql
environment:
- SPRING_DATASOURCE_URL=jdbc:mysql://mysql:3306/oauth2_demo
resource-server:
build:
context: ./resource-server
container_name: oauth2-resource-server
ports:
- "8082:8082"
depends_on:
- auth-server
这个完整的OAuth2实现包含了:
- 授权服务器:支持所有主流授权模式(密码、客户端、授权码、刷新令牌)
- 资源服务器:保护API资源,支持基于角色的访问控制
- 客户端应用:演示如何集成OAuth2认证
关键特性:
- JWT令牌支持
- 数据库存储客户端信息和令牌
- 安全的密码加密(BCrypt)
- 完善的错误处理
- 可扩展架构
生产环境注意事项:
- 使用非对称密钥加密JWT
- 配置HTTPS
- 实施适当的令牌撤销策略
- 考虑使用Spring Authorization Server(新替代方案)