4. 工程架构设计:基于 Spring Boot + Redis 的通用鉴权拦截器架构设计
在微服务与前后端分离架构中,鉴权拦截是保障后端接口安全的第一道防线。本章将基于 Spring Boot 框架,结合 Redis 的高性能缓存能力,设计一套通用的鉴权拦截器架构。该架构采用“过滤器链 + 拦截器模式”的双层防护体系,兼顾全局过滤与精细化业务控制。
4.1 架构设计目标与核心原则
通用鉴权拦截器架构需满足以下核心目标:
| 目标 | 说明 | 实现手段 |
|---|---|---|
| 高性能 | 鉴权操作不能成为接口瓶颈 | Redis 缓存 Token 与权限数据,减少 DB 查询 |
| 通用性 | 一套代码适配多个业务模块 | 基于注解 + 配置的拦截规则引擎 |
| 可扩展 | 支持多种鉴权策略(JWT、OAuth2、Session) | 策略模式 + 过滤器链动态组合 |
| 低侵入 | 业务代码无需感知鉴权逻辑 | Spring 拦截器 + AOP 切面解耦 |
| 可观测 | 鉴权失败原因可追溯 | 统一异常处理 + Redis 操作日志 |
4.2 双层架构:过滤器链 + 拦截器模式
本架构采用两层设计,将全局性安全策略与业务级鉴权分离:
- 第一层:过滤器链(Filter Chain) — 基于
javax.servlet.Filter,在请求进入 Servlet 容器后、进入 Spring 容器前执行。负责全局性、无差别的安全处理,如:CORS 跨域、XSS 过滤、请求日志、IP 黑白名单、请求限流。 - 第二层:拦截器(Interceptor) — 基于
HandlerInterceptor,在请求匹配到具体 Controller 方法前执行。负责业务相关的鉴权逻辑,如:Token 校验、权限验证、角色判断、资源访问控制。
架构优势: 过滤器层处理“能不能进系统”,拦截器层处理“能不能用这个功能”。两层职责清晰,互不干扰,且均可利用 Redis 缓存加速。
4.3 核心组件:通用鉴权拦截器设计
拦截器是整个鉴权体系的核心,其内部采用 策略模式 组合多种鉴权方式。核心组件包括:
| 组件 | 职责 | Redis 交互 |
|---|---|---|
AuthInterceptor |
拦截器入口,解析请求头 Token,调用策略链 | 从 Redis 获取 Token 黑名单 |
TokenStrategy |
Token 校验策略接口(JWT / OAuth2 / Session) | 校验 Token 签名与过期时间 |
PermissionStrategy |
权限校验策略接口(RBAC / ABAC) | 从 Redis 获取用户权限缓存 |
RateLimitStrategy |
接口限流策略(可选) | 基于 Redis 计数器 + Lua 脚本 |
AuthContextHolder |
线程级上下文,存储当前用户信息 | 无直接交互,由策略填充 |
设计要点: 使用
ThreadLocal 实现 AuthContextHolder,在拦截器校验通过后将用户信息存入,业务层通过静态方法获取,避免参数传递污染。
4.4 过滤器链配置与实现
过滤器链采用 Spring Boot 的 FilterRegistrationBean 注册,并指定执行顺序。以下为典型配置:
@Configuration
public class FilterChainConfig {
@Bean
public FilterRegistrationBean<CorsFilter> corsFilter() {
FilterRegistrationBean<CorsFilter> bean = new FilterRegistrationBean<>();
bean.setFilter(new CorsFilter());
bean.addUrlPatterns("/*");
bean.setOrder(1); // 最高优先级
return bean;
}
@Bean
public FilterRegistrationBean<XssFilter> xssFilter() {
FilterRegistrationBean<XssFilter> bean = new FilterRegistrationBean<>();
bean.setFilter(new XssFilter());
bean.addUrlPatterns("/*");
bean.setOrder(2);
return bean;
}
@Bean
public FilterRegistrationBean<IpBlacklistFilter> ipBlacklistFilter() {
FilterRegistrationBean<IpBlacklistFilter> bean = new FilterRegistrationBean<>();
bean.setFilter(new IpBlacklistFilter(redisTemplate));
bean.addUrlPatterns("/*");
bean.setOrder(3);
return bean;
}
}
注意: 过滤器链中不要执行耗时操作(如复杂数据库查询),否则会阻塞整个请求链路。IP 黑名单等数据应全量缓存到 Redis,或使用布隆过滤器加速判断。
4.5 拦截器注册与路径匹配
拦截器通过 WebMvcConfigurer 注册,可灵活配置拦截与放行路径:
@Configuration
public class InterceptorConfig implements WebMvcConfigurer {
@Autowired
private AuthInterceptor authInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(authInterceptor)
.addPathPatterns("/api/**") // 拦截所有 API 请求
.excludePathPatterns(
"/api/auth/login", // 登录接口放行
"/api/auth/register", // 注册接口放行
"/api/public/**", // 公共资源放行
"/swagger-ui/**", // Swagger 文档放行
"/v3/api-docs/**" // OpenAPI 文档放行
)
.order(1); // 拦截器执行顺序
}
}
4.6 Redis 缓存策略与数据结构设计
Redis 在本架构中承担三大缓存角色:Token 黑名单、用户权限缓存、接口限流计数器。推荐的数据结构如下:
| 缓存用途 | Redis 数据结构 | Key 设计 | 过期策略 |
|---|---|---|---|
| Token 黑名单 | Set | auth:blacklist:{token} |
TTL = Token 剩余有效期 |
| 用户权限缓存 | String (JSON) | auth:permissions:{userId} |
TTL = 30 分钟,主动刷新 |
| 接口限流 | String (计数器) | auth:ratelimit:{ip}:{api} |
TTL = 1 秒 / 1 分钟(按策略) |
| 用户会话 | Hash | auth:session:{sessionId} |
TTL = 30 分钟,滑动续期 |
4.7 鉴权拦截器核心代码实现
以下为通用鉴权拦截器的核心骨架,展示了策略模式的调用流程:
@Component
public class AuthInterceptor implements HandlerInterceptor {
@Autowired
private TokenStrategy tokenStrategy;
@Autowired
private PermissionStrategy permissionStrategy;
@Autowired
private RateLimitStrategy rateLimitStrategy;
@Autowired
private StringRedisTemplate redisTemplate;
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler) throws Exception {
// 1. 从请求头提取 Token
String token = request.getHeader("Authorization");
if (token == null || token.isEmpty()) {
throw new AuthException("缺少认证令牌");
}
// 2. 检查 Token 是否在黑名单中(Redis Set 查询)
Boolean isBlacklisted = redisTemplate.opsForSet()
.isMember("auth:blacklist:" + token, "1");
if (Boolean.TRUE.equals(isBlacklisted)) {
throw new AuthException("令牌已被吊销");
}
// 3. 策略模式:Token 校验
AuthUser user = tokenStrategy.validate(token);
// 4. 策略模式:权限校验(从 Redis 获取权限缓存)
permissionStrategy.check(user, request.getRequestURI());
// 5. 策略模式:接口限流(Redis Lua 脚本)
rateLimitStrategy.check(request.getRemoteAddr(), request.getRequestURI());
// 6. 将用户信息存入线程上下文
AuthContextHolder.setUser(user);
return true;
}
@Override
public void afterCompletion(HttpServletRequest request,
HttpServletResponse response,
Object handler, Exception ex) {
// 清理线程上下文,防止内存泄漏
AuthContextHolder.clear();
}
}
4.8 异常处理与统一响应
鉴权拦截器中抛出的异常,需通过全局异常处理器统一转换为标准响应格式:
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(AuthException.class)
public ResponseEntity<ErrorResponse> handleAuthException(AuthException e) {
ErrorResponse error = new ErrorResponse(
HttpStatus.UNAUTHORIZED.value(),
e.getMessage(),
System.currentTimeMillis()
);
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(error);
}
@ExceptionHandler(ForbiddenException.class)
public ResponseEntity<ErrorResponse> handleForbidden(ForbiddenException e) {
ErrorResponse error = new ErrorResponse(
HttpStatus.FORBIDDEN.value(),
e.getMessage(),
System.currentTimeMillis()
);
return ResponseEntity.status(HttpStatus.FORBIDDEN).body(error);
}
@ExceptionHandler(RateLimitException.class)
public ResponseEntity<ErrorResponse> handleRateLimit(RateLimitException e) {
ErrorResponse error = new ErrorResponse(
429, // Too Many Requests
"请求过于频繁,请稍后重试",
System.currentTimeMillis()
);
return ResponseEntity.status(429).body(error);
}
}
工程实践: 建议在
AuthException 中携带错误码(如 TOKEN_EXPIRED、TOKEN_INVALID、PERMISSION_DENIED),方便前端根据错误码做差异化处理,而非仅依赖 HTTP 状态码。
4.9 架构总结与最佳实践
- 过滤器链优先处理全局安全:CORS、XSS、IP 黑名单等无差别策略放在过滤器层,避免进入 Spring 容器增加开销。
- 拦截器专注业务鉴权:Token 校验、权限判断、限流等与业务相关的逻辑放在拦截器层,便于精细化配置。
- Redis 缓存是性能基石:Token 黑名单使用 Set 结构,权限缓存使用 String 序列化,限流计数器使用 Lua 脚本保证原子性。
- 策略模式实现可扩展:每种鉴权方式独立为策略接口,新增鉴权方式时无需修改拦截器核心代码。
- 线程上下文及时清理:
afterCompletion中必须清除ThreadLocal,防止内存泄漏或线程复用导致数据错乱。