一、OAuth2.0 授权码模式深度解析
1. 完整交互时序图(含PKCE增强)

2. 关键安全增强措施
PKCE(RFC 7636)防中间人攻击
String codeVerifier = generateRandomString(64);
String codeChallenge = Base64.getUrlEncoder()
.withoutPadding()
.encodeToString(
MessageDigest.getInstance("SHA-256")
.digest(codeVerifier.getBytes())
);
String authUrl = authEndpoint +
"?response_type=code" +
"&client_id=web" +
"&code_challenge=" + codeChallenge +
"&code_challenge_method=S256";
JWT 密钥管理最佳实践
openssl genrsa -out private.key 2048
openssl rsa -in private.key -pubout -out public.key
{
"keys": [{
"kty": "RSA",
"use": "sig",
"kid": "2023-08",
"n": "modulus_base64...",
"e": "AQAB"
}]
}
二、Spring Security OAuth2 深度配置
1. 授权服务器完整配置
@Configuration
@EnableAuthorizationServer
public class AuthServerConfig extends AuthorizationServerConfigurerAdapter {
@Autowired
private AuthenticationManager authenticationManager;
@Autowired
private DataSource dataSource;
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.jdbc(dataSource)
.passwordEncoder(passwordEncoder());
}
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
endpoints
.tokenStore(tokenStore())
.accessTokenConverter(jwtAccessTokenConverter())
.authenticationManager(authenticationManager)
.tokenEnhancer(tokenEnhancerChain())
.pathMapping("/oauth/token", "/auth/v1/token");
}
@Bean
public TokenEnhancerChain tokenEnhancerChain() {
TokenEnhancerChain chain = new TokenEnhancerChain();
chain.setTokenEnhancers(Arrays.asList(
new CustomTokenEnhancer(),
jwtAccessTokenConverter()
));
return chain;
}
@Bean
public JwtAccessTokenConverter jwtAccessTokenConverter() {
JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
converter.setKeyPair(keyPair());
return converter;
}
@Bean
public KeyPair keyPair() {
KeyStoreKeyFactory factory = new KeyStoreKeyFactory(
new ClassPathResource("keystore.jks"),
"keystore_password".toCharArray()
);
return factory.getKeyPair("oauth2");
}
}
2. 资源服务器动态JWKS配置
@Bean
public JwtDecoder jwtDecoder() {
return NimbusJwtDecoder.withJwkSetUri("https://auth-server.com/oauth/jwks")
.jwsAlgorithm(SignatureAlgorithm.RS256)
.build();
}
三、JWT 自动续签生产级实现
1. 刷新令牌流程

2. 服务端刷新令牌控制
oauth2:
token:
access:
validity: 3600
refresh:
validity: 2592000
reuse: false
3. 客户端自动续签实现(React示例)
const api = axios.create({
baseURL: '/api',
timeout: 5000
});
api.interceptors.request.use(config => {
const token = localStorage.getItem('access_token');
if (token) {
config.headers.Authorization = `Bearer ${
token}`;
}
return config;
});
api.interceptors.response.use(
response => response,
async error => {
const originalRequest = error.config;
if (error.response.status === 401 && !originalRequest._retry) {
originalRequest._retry = true;
try {
const response = await axios.post('/auth/token', {
grant_type: 'refresh_token',
refresh_token: localStorage.getItem('refresh_token')
});
localStorage.setItem('access_token', response.data.access_token);
localStorage.setItem('refresh_token', response.data.refresh_token);
originalRequest.headers.Authorization = `Bearer ${
response.data.access_token}`;
return api(originalRequest);
} catch (refreshError) {
window.location.href = '/login';
return Promise.reject(refreshError);
}
}
return Promise.reject(error);
}
);
四、安全加固方案
1. JWT 防篡改措施
public class CustomJwtValidator implements OAuth2TokenValidator<Jwt> {
@Override
public OAuth2TokenValidatorResult validate(Jwt jwt) {
if (!"https://auth-server.com".equals(jwt.getIssuer())) {
return OAuth2TokenValidatorResult.failure("Invalid issuer");
}
if (Instant.now().isAfter(jwt.getExpiresAt())) {
return OAuth2TokenValidatorResult.failure("Token expired");
}
return OAuth2TokenValidatorResult.success();
}
}
@Bean
public JwtDecoder jwtDecoder() {
NimbusJwtDecoder decoder = NimbusJwtDecoder.withJwkSetUri(jwkSetUrl).build();
decoder.setJwtValidator(new DelegatingOAuth2TokenValidator<>(
new JwtTimestampValidator(),
new CustomJwtValidator()
));
return decoder;
}
2. 动态客户端注册(RFC 7591)
@PostMapping("/register")
public ClientRegistration register(@Valid @RequestBody ClientRegistrationRequest request) {
String clientId = UUID.randomUUID().toString();
String clientSecret = generateRandomString(64);
clientRepository.save(new Client(
clientId,
passwordEncoder.encode(clientSecret),
request.getRedirectUris(),
request.getGrantTypes()
));
return new ClientRegistration(clientId, clientSecret);
}
五、全链路监控与运维
1. Prometheus监控指标
metrics:
- name: oauth2_token_requests
type: Counter
labels: [grant_type, client_id]
help: "Total OAuth2 token requests"
- name: oauth2_token_refreshes
type: Counter
labels: [client_id, success]
help: "Refresh token attempts"
2. 日志审计配置
@Aspect
@Component
public class OAuth2AuditLogAspect {
@AfterReturning(
pointcut = "execution(* org.springframework.security.oauth2.provider.endpoint.TokenEndpoint.*(..))",
returning = "result"
)
public void logTokenIssuance(JoinPoint jp, Object result) {
OAuth2AccessToken token = (OAuth2AccessToken) result;
log.info("Token issued for client {} with scopes {}",
SecurityContextHolder.getContext().getAuthentication().getName(),
token.getScope());
}
}
3. 令牌撤销黑名单(Redis实现)
@Bean
public TokenStore tokenStore(RedisConnectionFactory factory) {
RedisTokenStore store = new RedisTokenStore(factory);
store.setPrefix("oauth2:");
return store;
}
@PostMapping("/revoke")
public void revokeToken(@RequestParam String token) {
tokenStore.removeAccessToken(token);
redisTemplate.opsForValue().set(
"token_blacklist:" + token,
"revoked",
1, TimeUnit.HOURS
);
}
六、性能优化实战
1. JWT 本地验证(减少网络调用)
@Bean
public JwtDecoder jwtDecoder() {
Resource resource = new ClassPathResource("public.key");
String publicKey = new String(resource.getInputStream().readAllBytes());
RSAPublicKey key = (RSAPublicKey) KeyFactory.getInstance("RSA")
.generatePublic(new X509EncodedKeySpec(Base64.getDecoder().decode(publicKey)));
return NimbusJwtDecoder.withPublicKey(key).build();
}
2. 令牌缓存策略
spring:
redis:
host: redis-cluster.example.com
timeout: 200ms
lettuce:
pool:
max-active: 50
max-idle: 20
3. 分布式限流(Guava + Redis)
@Bean
public RateLimiter tokenRateLimiter() {
return RateLimiter.create(10, 1, TimeUnit.MINUTES);
}
@PostMapping("/token")
public ResponseEntity<OAuth2AccessToken> issueToken(Principal principal) {
if (!tokenRateLimiter.tryAcquire()) {
throw new TooManyRequestsException("Rate limit exceeded");
}
}
七、故障排查手册
| 现象 |
排查工具 |
解决方案 |
| 授权码无效 |
检查数据库oauth_code表 |
清理过期code(<10分钟) |
| JWT校验失败 |
对比JWKS公钥与私钥 |
轮换密钥后更新JWKS端点 |
| 刷新令牌频繁失效 |
审计日志分析refresh_token使用 |
配置refreshTokenReuse=false |
| 高并发下令牌服务超时 |
Redis监控/QPS统计 |
增加集群节点+连接池优化 |
八、完整示例项目