Spring自定义过滤器兼容Swagger UI并实现licenseKey校验的优化方案
优化SpringBoot中LicenseKey校验过滤器的实现思路
现有实现的问题点
- 硬编码路径:Swagger相关路径写死在代码中,后续路径变更或新增白名单需修改代码重新部署
- 校验逻辑缺失:仅获取licenseKey但未做有效性校验,未实现“必须携带有效licenseKey”的核心要求
- 日志不规范:混用
System.out.println和SLF4J日志,不利于线上问题排查 - 拦截范围无控制:
@Component注解导致过滤器对所有请求生效,未精准控制拦截目标 - 异常处理缺失:licenseKey校验失败时,未向客户端返回标准错误响应
更优实现方案
方案一:基于Spring Security实现(推荐)
Spring Security提供成熟的请求拦截、权限校验机制,比自定义Filter更规范、易维护:
- 配置白名单路径,将Swagger相关路径纳入无需校验范围
- 自定义校验逻辑实现licenseKey有效性验证
- 通过SecurityFilterChain控制全局拦截规则
示例代码:
@Configuration @EnableWebSecurity public class SecurityConfig { private final CustomUserDetailsService userDetailsService; public SecurityConfig(CustomUserDetailsService userDetailsService) { this.userDetailsService = userDetailsService; } @Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http .csrf(csrf -> csrf.disable()) // 根据业务场景决定是否关闭CSRF .authorizeHttpRequests(auth -> auth // 白名单:Swagger相关路径直接放行 .requestMatchers("/app-name-service/swagger-ui/**", "/app-name-service/v3/api-docs/**").permitAll() // 其余所有请求需通过licenseKey校验 .anyRequest().authenticated() ) // 注入自定义licenseKey校验过滤器 .addFilterBefore(licenseKeyAuthFilter(), UsernamePasswordAuthenticationFilter.class); return http.build(); } @Bean public Filter licenseKeyAuthFilter() { return (servletRequest, servletResponse, filterChain) -> { HttpServletRequest request = (HttpServletRequest) servletRequest; HttpServletResponse response = (HttpServletResponse) servletResponse; String licenseKey = userDetailsService.getLicenseKey(request); // 执行licenseKey有效性校验 if (licenseKey == null || !userDetailsService.validateLicenseKey(licenseKey)) { response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); response.setContentType("application/json"); response.getWriter().write("{\"code\":401,\"message\":\"缺失或无效的licenseKey\"}"); return; } filterChain.doFilter(request, response); }; } }
方案二:改进自定义Filter实现
若不想引入Spring Security,可对原有Filter做针对性优化:
配置化白名单路径:通过
@ConfigurationProperties将白名单路径配置到配置文件,避免硬编码@Component @ConfigurationProperties(prefix = "filter.license.whitelist") public class CustomURLFilter implements Filter { private List<String> paths = new ArrayList<>(); // 省略getter/setter @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { HttpServletRequest request = (HttpServletRequest) servletRequest; HttpServletResponse response = (HttpServletResponse) servletResponse; String path = request.getRequestURI(); // 判断请求路径是否在白名单内 boolean isWhitelisted = paths.stream().anyMatch(path::startsWith); if (isWhitelisted) { filterChain.doFilter(request, response); return; } // 校验licenseKey有效性 String licenseKey = userDetailsService.getLicenseKey(request); if (licenseKey == null || !userDetailsService.validateLicenseKey(licenseKey)) { response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); response.setContentType("application/json"); response.getWriter().write("{\"code\":401,\"message\":\"缺失或无效的licenseKey\"}"); return; } filterChain.doFilter(request, response); } }对应
application.yml配置:filter: license: whitelist: - "/app-name-service/swagger-ui" - "/app-name-service/v3/api-docs"精准控制拦截范围:使用
@WebFilter指定拦截的URL模式,避免无差别拦截@WebFilter(urlPatterns = {"/app-name-service/**"}) // 仅拦截服务业务路径 public class CustomURLFilter implements Filter { // 省略其他代码 }同时在启动类添加
@ServletComponentScan注解,开启Servlet组件扫描规范日志与异常处理:统一使用SLF4J日志替代
System.out.println;校验失败时返回标准HTTP错误响应,而非直接放行
额外优化建议
- 将licenseKey校验逻辑封装为独立服务类,避免与Filter耦合
- 若校验涉及数据库或远程调用,可对有效licenseKey做缓存,减少重复校验开销
- Swagger后续配置授权时,可集成到统一的权限校验流程中,保持逻辑一致性
内容的提问来源于stack exchange,提问作者PythonLearner
相关产品推荐
相关产品推荐

