Spring Boot中如何在整个Rest Controller上禁用JwtFilter?
我在Spring Boot项目中实现了基于JWT的安全机制,创建了两个过滤器并加入过滤器链,分别用于生成JWT令牌和验证用户是否持有令牌。由于是测试项目,端点不多,我重写了验证令牌的过滤器的shouldNotFilter方法,使其在注册、登录等请求上不生效,代码如下:
@Override protected boolean shouldNotFilter(HttpServletRequest request) { if(request.getRequestURI().startsWith("/login") || request.getRequestURI().startsWith("/api/users/registration")) return true; return false; }
但目前只有这两个端点,若需要在整个Controller上禁用该过滤器,是否有可用的注解实现?还是需要删除该重写方法,改用.antMatchers(端点列表).permitAll()?
AuthController代码
@RestController @RequestMapping("/api/users") public class AuthController { private final UserFacadeService userFacadeService; @Autowired public AuthController(UserFacadeService userFacadeService) { this.userFacadeService = userFacadeService; } @PostMapping("/registration") public ResponseEntity<Object> signUp(@RequestBody @Valid UserDTO userDTO){ return ResponseEntity .status(HttpStatus.CREATED) .body(LocalDateTime.now() + " user created: " + this.userFacadeService.saveUser(userDTO)); } }
SecurityConfig代码
@Configuration @EnableWebSecurity @EnableGlobalMethodSecurity(prePostEnabled = true) public class SecurityConfig { private final UserService userService; @Autowired @Lazy private LoginFilter loginFilter; private final JwtFilter jwtFilter; private final PasswordEncoder passwordEncoder; private final ExceptionHandlerFilter exFilter; @Autowired public SecurityConfig(UserService userService, JwtFilter jwtFilter, PasswordEncoder passwordEncoder, ExceptionHandlerFilter exFilter) { this.userService = userService; this.jwtFilter = jwtFilter; this.passwordEncoder = passwordEncoder; this.exFilter = exFilter; } @Bean public AuthenticationManager authenticationManager(){ DaoAuthenticationProvider dao = new DaoAuthenticationProvider(); dao.setUserDetailsService(userService); dao.setPasswordEncoder(passwordEncoder); return new ProviderManager(dao); } @Bean protected SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http .csrf() .disable() .sessionManagement() .sessionCreationPolicy(SessionCreationPolicy.STATELESS) .and() .authorizeRequests((auth)->auth .expressionHandler(webExpressionHandler()) .antMatchers(AUTH_WHITELIST) .permitAll() .antMatchers("/api/users/registration") .permitAll() .anyRequest() .authenticated() .and() .addFilterBefore(exFilter, ChannelProcessingFilter.class) .addFilterAt(loginFilter, BasicAuthenticationFilter.class) .addFilterAt(jwtFilter,BasicAuthenticationFilter.class) ); return http.build(); } // 实现角色层级需要定义SecurityExpressionHandler并加入过滤器链 private SecurityExpressionHandler<FilterInvocation> webExpressionHandler() { DefaultWebSecurityExpressionHandler defaultWebSecurityExpressionHandler = new DefaultWebSecurityExpressionHandler(); defaultWebSecurityExpressionHandler.setRoleHierarchy(roleHierarchy()); return defaultWebSecurityExpressionHandler; } // 定义角色层级,高权限角色可访问低权限角色的所有API @Bean public RoleHierarchyImpl roleHierarchy() { RoleHierarchyImpl roleHierarchy = new RoleHierarchyImpl(); roleHierarchy.setHierarchy("OPERATOR > CUSTOMER"); return roleHierarchy; } }
方案一:注解方式禁用过滤器(@WebFilter)
如果你的JWT过滤器继承自OncePerRequestFilter,可以直接给过滤器类添加@WebFilter注解,通过excludeUrlPatterns指定要排除的路径(即整个Controller的路径):
@WebFilter(urlPatterns = "/*", excludeUrlPatterns = "/api/users/**") public class JwtFilter extends OncePerRequestFilter { // 你的过滤器逻辑 }
同时需要在项目启动类上添加@ServletComponentScan,让Spring扫描到这个注解配置的过滤器。注意这种方式和Spring Security过滤器链的配置可能存在优先级冲突,需要确保规则一致。
方案二:统一在SecurityConfig中配置(推荐)
直接删除过滤器中重写的shouldNotFilter方法,在filterChain里通过antMatchers统一配置放行路径,这样更符合Spring Security的集中配置风格,后续扩展端点也更方便:
.authorizeRequests((auth)->auth .expressionHandler(webExpressionHandler()) .antMatchers(AUTH_WHITELIST, "/api/users/**") .permitAll() .anyRequest() .authenticated() // ... 其他配置 )
这样/api/users下的所有端点都会被自动放行,后续新增端点到该Controller时,无需修改过滤器代码,直接继承该路径规则即可。
方案三:方法级权限注解(细粒度控制)
如果需要针对单个方法放行,可以利用已开启的@EnableGlobalMethodSecurity(prePostEnabled = true),给Controller方法添加@PreAuthorize("permitAll()"):
@PostMapping("/registration") @PreAuthorize("permitAll()") public ResponseEntity<Object> signUp(@RequestBody @Valid UserDTO userDTO){ // 方法逻辑 }
不过这种方式是权限放行,过滤器仍会执行,只是权限校验直接通过。如果需要真正禁用过滤器执行,还是前两种方案更直接。
内容的提问来源于stack exchange,提问作者Rezel

