本文目录导读:

我将为您提供一个完整的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(新替代方案)
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播