题目
设计基于Spring Security OAuth2的分布式系统安全架构,实现自定义JWT声明和动态权限控制
信息
- 类型:问答
- 难度:⭐⭐⭐
考点
OAuth2授权服务器配置,自定义JWT声明,动态权限控制,资源服务器集成,Spring Security过滤器链
快速回答
实现该架构需要:
- 配置
@EnableAuthorizationServer并扩展AuthorizationServerConfigurerAdapter - 实现自定义
TokenEnhancer添加JWT声明 - 通过
@EnableResourceServer配置资源服务器 - 实现
GlobalMethodSecurity动态权限控制 - 自定义
AccessDeniedHandler和AuthenticationEntryPoint处理异常
架构核心组件
分布式安全架构包含:
- 授权服务器:负责颁发JWT令牌
- 资源服务器:验证令牌并保护API
- 自定义声明:在JWT中添加业务相关数据
- 动态权限:基于数据库实时更新权限规则
关键实现步骤
1. 自定义JWT声明
public class CustomTokenEnhancer implements TokenEnhancer {
@Override
public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {
Map<String, Object> additionalInfo = new HashMap<>();
// 添加自定义声明(例:租户ID)
additionalInfo.put("tenant_id", "TENANT_123");
((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(additionalInfo);
return accessToken;
}
}配置授权服务器:
@Configuration
@EnableAuthorizationServer
public class AuthServerConfig extends AuthorizationServerConfigurerAdapter {
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
TokenEnhancerChain chain = new TokenEnhancerChain();
chain.setTokenEnhancers(Arrays.asList(new CustomTokenEnhancer(), jwtAccessTokenConverter()));
endpoints.tokenEnhancer(chain);
}
@Bean
public JwtAccessTokenConverter jwtAccessTokenConverter() {
JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
converter.setSigningKey("secret_key"); // 生产环境使用非对称加密
return converter;
}
}2. 动态权限控制
实现权限服务:
@Service
public class DynamicPermissionService implements PermissionEvaluator {
@Override
public boolean hasPermission(Authentication auth, Object targetId, Object permission) {
// 从数据库实时加载权限规则
return checkPermission(auth.getName(), (String) permission);
}
private boolean checkPermission(String username, String requiredPermission) {
// 数据库查询逻辑(示例)
return userRepository.findPermissions(username)
.contains(requiredPermission);
}
}启用方法级安全控制:
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class MethodSecurityConfig extends GlobalMethodSecurityConfiguration {
@Override
protected MethodSecurityExpressionHandler createExpressionHandler() {
DefaultMethodSecurityExpressionHandler handler =
new DefaultMethodSecurityExpressionHandler();
handler.setPermissionEvaluator(new DynamicPermissionService());
return handler;
}
}3. 资源服务器配置
@Configuration
@EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
@Override
public void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/api/public/**").permitAll()
.antMatchers("/api/admin/**").hasRole("ADMIN")
.anyRequest().authenticated()
.and()
.exceptionHandling()
.accessDeniedHandler(customAccessDeniedHandler());
}
@Bean
public AccessDeniedHandler customAccessDeniedHandler() {
return (request, response, ex) -> {
response.setContentType("application/json");
response.sendError(HttpStatus.FORBIDDEN.value(), "Access Denied");
};
}
}最佳实践
- JWT安全:使用RS256非对称加密,定期轮换签名密钥
- 声明设计:避免在JWT中存储敏感数据,声明大小控制在4KB以内
- 权限缓存:对动态权限查询结果使用Guava Cache,设置TTL=5分钟
- 令牌撤销:实现黑名单机制处理注销的令牌
常见错误
| 错误类型 | 解决方案 |
|---|---|
| JWT签名验证失败 | 确保资源服务器与授权服务器使用相同的密钥/证书 |
| 权限变更延迟 | 在权限服务中添加@CacheEvict逻辑 |
| 跨服务权限不一致 | 使用中央权限服务而非本地缓存 |
| 令牌泄露风险 | 设置短有效期(建议≤1小时)+刷新令牌机制 |
扩展知识
- OAuth2最佳实践:PKCE扩展防止授权码拦截攻击
- 性能优化:在资源服务器使用JwkTokenStore避免远程校验
- 新趋势:Opaque令牌替代JWT满足更高安全需求
- 监控要点:跟踪JWT声明大小增长率、权限检查延迟