题目
设计基于Spring Security的分布式多租户系统认证授权方案
信息
- 类型:问答
- 难度:⭐⭐⭐
考点
Spring Security架构深度理解, JWT与OAuth2.0集成, 多租户安全隔离, 分布式系统认证授权设计
快速回答
实现分布式多租户系统的安全方案需要:
- 使用Spring Security OAuth2 Resource Server支持JWT认证
- 自定义租户解析策略(从JWT claims/请求头提取租户ID)
- 实现基于租户ID的动态数据隔离(DAO层/SQL拦截)
- 结合Spring Security方法级授权(
@PreAuthorize) - 设计分布式令牌验证机制(JWT自验证 vs 集中式校验)
1. 核心架构设计
认证流程:客户端携带JWT访问资源服务器 → Spring Security过滤器链验证签名/有效期 → 自定义租户解析器提取租户ID → 租户上下文绑定 → 数据访问层自动应用租户隔离
组件关系图:
(图示:OAuth2授权服务器独立部署,资源服务器解析JWT并实现多租户隔离)
2. 关键代码实现
自定义租户解析器
public class TenantJwtConverter implements Converter<Jwt, AbstractAuthenticationToken> {
@Override
public AbstractAuthenticationToken convert(Jwt jwt) {
// 从JWT claims提取租户ID
String tenantId = jwt.getClaim("tenant_id");
// 存储到SecurityContext
TenantContext.setCurrentTenant(tenantId);
// 构建认证对象
return new JwtAuthenticationToken(jwt, AuthorityUtils.NO_AUTHORITIES);
}
}多租户数据隔离(MyBatis拦截器示例)
@Interceptor
public class TenantInterceptor implements Interceptor {
@Override
public Object intercept(Invocation invocation) {
// 动态添加租户过滤条件
MappedStatement ms = invocation.getMappedStatement();
if (isFilteredOperation(ms)) {
ParameterHandler ph = invocation.getParameterHandler();
Map<String, Object> params = (Map) ph.getParameterObject();
params.put("tenantId", TenantContext.getCurrentTenant());
}
return invocation.proceed();
}
}方法级租户授权控制
@Service
public class ProductService {
// 确保操作租户与当前租户匹配
@PreAuthorize("@tenantSecurity.checkTenant(#product.tenantId)")
public void updateProduct(Product product) {
// ...业务逻辑
}
}
@Component
public class TenantSecurity {
public boolean checkTenant(String targetTenant) {
return targetTenant.equals(TenantContext.getCurrentTenant());
}
}3. 最佳实践
- 租户ID传递:优先使用JWT claim携带租户ID(避免前端篡改)
- 数据隔离策略:
- 方案A:数据库级隔离(独立schema) - 安全性高但运维复杂
- 方案B:应用级隔离(所有SQL自动添加tenant_id条件) - 本方案采用
- 令牌验证优化:
- 使用JWT本地验证(
jwtDecoder配置公钥)避免远程调用 - 通过Redis维护令牌吊销列表(RPT)应对令牌泄露
- 使用JWT本地验证(
4. 常见错误与规避
| 错误场景 | 风险 | 解决方案 |
|---|---|---|
| 未清除线程上下文中的租户ID | 租户数据泄露 | 实现OncePerRequestFilter清理上下文 |
| 直接信任前端传递的租户ID | 越权访问 | 强制从认证对象获取租户ID |
| 忽略JWT签名验证 | 伪造令牌攻击 | 配置JwtDecoder使用公钥验证 |
5. 扩展知识
- 性能优化:
- 使用JWK Set URI动态轮换密钥(避免服务重启)
- 租户信息缓存:Redis存储租户元数据(如权限配置)
- 混合认证支持:
- 兼容API密钥认证:实现
AuthenticationProvider链式处理 - 方案示例:
JwtAuthenticationProvider与ApiKeyAuthenticationProvider并存
- 兼容API密钥认证:实现
- 安全加固:
- 防范JWT替换攻击:绑定访问IP与设备指纹
- 敏感操作强制二次认证(Step-up Authentication)