如何仅对受限HTTP路径应用Spring Security安全过滤器
问题根因
你的过滤器全局生效的核心原因有两个:
- 你给
JwtFilter加了@Component注解,Spring Boot会自动将所有实现了Filter接口且被Spring管理的Bean注册到全局过滤器链,这个链和Spring Security的过滤器链是独立的,无论你在SecurityConfig里怎么配置路径匹配,全局注册的过滤器都会作用于所有请求。 - 你当前
JwtFilter内部逻辑存在bug,token变量未赋值就直接调用jwtUtils.extractUsername(token)和jwtUtils.validateToken(token),即使路径配置正确也会出现校验失败的问题。
解决方案
方案1:移除自动注册,仅在Spring Security链中手动添加
- 去掉
JwtFilter类上的@Component注解,阻止Spring自动将其注册到全局过滤器链 - 调整
SecurityConfig中的配置,手动实例化过滤器:
// SecurityConfig中手动创建JwtFilter实例,避免自动注册 private JwtFilter jwtFilter() { return new JwtFilter(); } @Override protected void configure(final HttpSecurity http) throws Exception { http.csrf().disable() .antMatcher("/admin/**") .authorizeRequests() .anyRequest().authenticated() .and() .addFilterBefore(jwtFilter(), UsernamePasswordAuthenticationFilter.class); }
此时JwtFilter只会作用于/admin/**开头的路径,其余路径不会触发该过滤器。
方案2:保留@Component注解,禁用全局自动注册
如果你需要保留@Component方便注入依赖,可以新增FilterRegistrationBean配置关闭该过滤器的全局注册:
@Configuration public class FilterConfig { @Bean public FilterRegistrationBean<JwtFilter> disableJwtFilterGlobalRegistration(JwtFilter jwtFilter) { FilterRegistrationBean<JwtFilter> registration = new FilterRegistrationBean<>(jwtFilter); registration.setEnabled(false); return registration; } }
配置完成后,JwtFilter仅会在你手动添加的Spring Security过滤器链中生效,不会拦截所有请求。
方案3:过滤器层面控制路径匹配
如果需要更灵活的路径控制,可以重写OncePerRequestFilter的shouldNotFilter方法,直接在过滤器内部判断是否要执行校验逻辑:
@Override protected boolean shouldNotFilter(HttpServletRequest request) throws ServletException { AntPathMatcher pathMatcher = new AntPathMatcher(); // 以下路径不执行JWT校验 return pathMatcher.match("/login", request.getServletPath()) || pathMatcher.match("/register", request.getServletPath()) || !pathMatcher.match("/admin/**", request.getServletPath()); }
这种方式无需修改注册逻辑,即使过滤器被全局注册也可以正常排除不需要校验的路径。
修复JwtFilter逻辑bug
调整doFilterInternal中的token提取逻辑,避免空指针异常:
@Override protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException { String authorizationHeader = httpServletRequest.getHeader("Authorization"); String token = null; String userName = null; // 正确提取Bearer token if (authorizationHeader != null && authorizationHeader.startsWith("Bearer ")) { token = authorizationHeader.substring(7); userName = jwtUtils.extractUsername(token); } // 仅当token不为空时才执行校验 if (token != null) { if (!jwtUtils.validateToken(token)) { httpServletResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED); return; } if (userName != null && SecurityContextHolder.getContext().getAuthentication() == null) { UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(userName, null); authToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(httpServletRequest)); SecurityContextHolder.getContext().setAuthentication(authToken); } } filterChain.doFilter(httpServletRequest, httpServletResponse); }
内容的提问来源于stack exchange,提问作者ilphrine
相关产品推荐
相关产品推荐

