You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何基于HttpServletRequest配置Spring Boot Web安全,按请求头跳过JWT认证

基于HttpServletRequest配置Spring Boot Web安全,实现按请求头跳过JWT认证

你的需求是:当请求头包含sourceSystem且值为External时,跳过JWT认证;其他情况必须进行JWT认证。但当前的配置存在核心问题——configure(HttpSecurity)方法是在Spring容器启动时执行的,并非每次请求触发,所以直接在这里获取HttpServletRequest根本拿不到当前请求的头信息,逻辑完全不生效。

下面是两种可行的修正方案:

方案一:自定义RequestMatcher配置授权规则

通过自定义RequestMatcher来匹配需要跳过JWT的请求,在Spring Security的授权规则中直接配置放行。

1. 自定义RequestMatcher

public class SkipJwtRequestMatcher implements RequestMatcher {
    @Override
    public boolean matches(HttpServletRequest request) {
        // 读取请求头,判断是否符合跳过条件(忽略大小写可根据需求调整)
        String sourceSystem = request.getHeader("sourceSystem");
        return "EXTERNAL".equalsIgnoreCase(sourceSystem);
    }
}

2. 修改WebSecurityConfig配置

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint;

    @Autowired
    private UserDetailsService jwtUserDetailsService;

    @Autowired
    private JwtRequestFilter jwtRequestFilter;

    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(jwtUserDetailsService).passwordEncoder(passwordEncoder());
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    @Override
    protected void configure(HttpSecurity httpSecurity) throws Exception {
        // 实例化自定义匹配器
        RequestMatcher skipJwtMatcher = new SkipJwtRequestMatcher();

        httpSecurity.csrf().disable()
                .authorizeRequests()
                // 固定放行的路径
                .antMatchers("/test").permitAll()
                // OPTIONS请求统一放行
                .antMatchers(HttpMethod.OPTIONS, "/**").permitAll()
                // 符合跳过条件的请求直接放行
                .requestMatchers(skipJwtMatcher).permitAll()
                // 其余所有请求必须认证
                .anyRequest().authenticated()
                .and()
                .exceptionHandling().authenticationEntryPoint(jwtAuthenticationEntryPoint)
                .and()
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);

        httpSecurity.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);
        httpSecurity.cors();
    }
}

方案二:在JWT过滤器中直接判断跳过

修改JwtRequestFilter,在每次请求进入过滤器时先判断是否需要跳过认证,符合条件则直接放行,不执行JWT验证逻辑。

@Component
public class JwtRequestFilter extends OncePerRequestFilter {

    // 注入你的JWT工具类、UserDetailsService等依赖
    @Autowired
    private JwtTokenUtil jwtTokenUtil;
    @Autowired
    private UserDetailsService jwtUserDetailsService;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
            throws ServletException, IOException {
        // 先判断是否需要跳过JWT认证
        String sourceSystem = request.getHeader("sourceSystem");
        if ("EXTERNAL".equalsIgnoreCase(sourceSystem)) {
            // 直接放行,不处理JWT验证
            chain.doFilter(request, response);
            return;
        }

        // 原有JWT验证逻辑继续执行
        String requestTokenHeader = request.getHeader("Authorization");

        String username = null;
        String jwtToken = null;

        // JWT Token在Authorization头中,格式为Bearer token
        if (requestTokenHeader != null && requestTokenHeader.startsWith("Bearer ")) {
            jwtToken = requestTokenHeader.substring(7);
            try {
                username = jwtTokenUtil.getUsernameFromToken(jwtToken);
            } catch (IllegalArgumentException e) {
                System.out.println("Unable to get JWT Token");
            } catch (ExpiredJwtException e) {
                System.out.println("JWT Token has expired");
            }
        } else {
            logger.warn("JWT Token does not begin with Bearer String");
        }

        // 验证token有效性
        if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
            UserDetails userDetails = this.jwtUserDetailsService.loadUserByUsername(username);
            if (jwtTokenUtil.validateToken(jwtToken, userDetails)) {
                UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(
                        userDetails, null, userDetails.getAuthorities());
                usernamePasswordAuthenticationToken
                        .setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
                SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
            }
        }
        chain.doFilter(request, response);
    }
}

方案对比

  • 方案一更贴合Spring Security的配置范式,将授权规则集中在配置类中管理,逻辑清晰。
  • 方案二更灵活,适合需要在过滤器层做额外处理的场景,比如跳过认证时需要记录日志等操作。

内容的提问来源于stack exchange,提问作者user3744825

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.26 20:36:01