[072][验证码模块]验证码认证拦截器实现解析与扩展实战
本文章代码: gitee , gitcode , github
1. 注解定义详解
@Target({
ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface CaptchaAuth {
String key() default DefaultConsts.HTTP_HEADER_CAPTCHA_KEY;
String category() default DefaultConsts.HTTP_HEADER_CAPTCHA_CATEGORY;
String code() default DefaultConsts.HTTP_HEADER_CAPTCHA_CODE;
}
@Target支持类和方法级别,若类上存在注解,则该类所有方法均受保护(可通过方法注解覆盖)。- 三个属性分别对应验证码标识(如UUID)、类型(如
sms、image)、用户输入的码值,默认值指向全局常量,便于统一管理HTTP头名称。
2. 拦截器核心逻辑分析
public boolean preHandle(HttpServletRequest request, ...) {
CaptchaAuth captchaAuth = WebUtils.getHandlerMethodAnnotation(handler, CaptchaAuth.class);
if (captchaAuth != null) {
String key = HeaderUtils.getHeader(request, DefaultConsts.HTTP_HEADER_CAPTCHA_KEY);
// ... 同上获取 category, code
if (StringUtils.isAnyBlank(key, category, code)) {
throw CaptchaErrorCode.CAPTCHA_PARAMETERS_INCOMPLETE.throwed();
}
if (!captchaServiceFactory.findService(category).verify(key, code)) {
throw CaptchaErrorCode.CAPTCHA_VERIFY_FAILURE.throwed();
}
}
return true;
}
关键点:
- 注解获取:
WebUtils自定义方法会先查找方法级注解,若不存在再查找类级,实现细粒度覆盖。 - 参数来源:完全依赖HTTP Header,而非请求体或参数,适用于RESTful API(通常Token放Header)。
- 工厂调用:
findService(category)返回CaptchaService实例,若category无效,工厂应抛出异常(如IllegalArgumentException),保证扩展性。 - 布尔返回值:
verify返回true表示验证通过,否则失败。
3. CaptchaServiceFactory 设计推测
虽然未提供工厂代码,但根据用法可推断其结构:
public interface CaptchaServiceFactory {
CaptchaService findService(String category);
}
public interface CaptchaService {
boolean verify(String key, String code);
}
工厂可用Map<String, CaptchaService>存储,或通过Spring依赖注入自动收集@Component实现类,按category区分。
4. 扩展点与最佳实践
- 自定义验证码类型:实现
CaptchaService接口,在verify中连接Redis/数据库校验,然后注册到工厂。 - 参数动态化:若某些接口希望从请求体获取参数,可修改拦截器逻辑,例如先用Header,若为空再降级到参数,但需注意安全。
- 性能优化:验证码校验通常涉及IO(Redis/DB),应设置合理超时,并可考虑异步处理(但拦截器同步较简单)。
- 日志记录:已有debug日志,建议在失败时记录warn级别,便于监控。
5. 使用示例
在Controller中:
@RestController
public class LoginController {
@PostMapping("/login")
@CaptchaAuth(category = "image") // 指定使用图片验证码
public Result login(@RequestBody LoginReq req) {
// 业务逻辑,此时验证码已通过
}
}
客户端需在Header中传递:
X-Captcha-Key: abc-123
X-Captcha-Category: image
X-Captcha-Code: 7X8Y
6. 潜在改进建议
- 支持忽略校验:某些场景下可能需要跳过类级别注解,可通过
@CaptchaAuth增加enabled属性。 - 缓存注解信息:每次请求都通过反射获取注解,可考虑在
WebUtils中增加缓存提升性能。 - 支持多种参数来源:可扩展为从请求参数、Cookie等获取,通过策略模式实现。
- 单元测试:应针对拦截器编写单元测试,使用
MockHttpServletRequest模拟Header,并验证工厂调用。
7. 总结
该实现巧妙地利用Spring拦截器和自定义注解,将验证码校验从业务代码剥离,既保证了代码整洁,又提供了良好的扩展性。开发者只需关注CaptchaService的具体实现,而认证流程由框架统一管理,是典型的企业级横切关注点解决方案。