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

Spring Security过滤器被重复调用,请求路径异常添加/api前缀

问题描述

基于Spring Boot + Security实现REST API无状态认证,向/api/login发起PUT请求时,控制器方法能正常调用,认证成功并生成token,但随后过滤器会以/api/api/login路径二次调用,导致整个链路出错。已通过Postman和IntelliJ HTTP客户端测试该端点。

控制器代码

@PutMapping("/api/login")
public LoginUserOutput loginUser(@Valid @RequestBody LoginUserInput loginUserInput) {
    final var authentication = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(loginUserInput.username(), loginUserInput.password()));
    final var user = (User) authentication.getPrincipal();

    final var claims = JwtClaimsSet.builder().subject(user.getUsername()).build();
    final var header = JwsHeader.with(MacAlgorithm.HS256).build();
    final var token = jwtEncoder.encode(JwtEncoderParameters.from(header, claims));

    return new LoginUserOutput(token.getTokenValue());
}

SecurityFilterChain配置

@Bean
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
    return http.csrf().disable()
            .authorizeHttpRequests().requestMatchers("/api/prometheus/**").hasRole("MONITORING").and().httpBasic()
            .and()
            .authorizeHttpRequests().requestMatchers(HttpMethod.PUT, "/api/login").permitAll()
            .and()
            .authorizeHttpRequests().anyRequest().authenticated()
            .and()
            .oauth2ResourceServer(OAuth2ResourceServerConfigurer::jwt)
            .sessionManagement()
            .sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
            .exceptionHandling((exceptions) -> exceptions.authenticationEntryPoint(new BearerTokenAuthenticationEntryPoint())
                    .accessDeniedHandler(new BearerTokenAccessDeniedHandler()))
            .build();
}

Spring Security DEBUG日志

2023-05-08T21:18:42.121+02:00 DEBUG 32155 --- [io-28080-exec-3] o.s.security.web.FilterChainProxy        : Securing PUT /api/login
2023-05-08T21:18:42.122+02:00 DEBUG 32155 --- [io-28080-exec-3] o.s.s.w.a.AnonymousAuthenticationFilter  : Set SecurityContextHolder to anonymous SecurityContext
2023-05-08T21:18:42.123+02:00 DEBUG 32155 --- [io-28080-exec-3] o.s.security.web.FilterChainProxy        : Secured PUT /api/login
Hibernate: select u1_0.id,u1_0.hashed_password,u1_0.roles,u1_0.username from users u1_0 where u1_0.username=?
2023-05-08T21:18:42.234+02:00 DEBUG 32155 --- [io-28080-exec-3] o.s.s.a.dao.DaoAuthenticationProvider    : Authenticated user
2023-05-08T21:18:42.235+02:00 DEBUG 32155 --- [io-28080-exec-3] o.s.security.web.FilterChainProxy        : Securing PUT /api/api/login
解决方案

1. 修复控制器路径冲突

检查控制器类是否标注了@RequestMapping("/api")前缀,如果有,方法上的@PutMapping("/api/login")会拼接成/api/api/login,导致路径重复。

  • 修复方式:将方法上的路径改为@PutMapping("/login"),或移除控制器类上的/api前缀,保证路径唯一。

2. 优化Security配置的链式调用

多次拆分authorizeHttpRequests()可能导致匹配逻辑混乱,合并为统一链式调用,确保/api/login的放行规则优先执行:

@Bean
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
    return http.csrf().disable()
            .authorizeHttpRequests(auth -> auth
                    .requestMatchers(HttpMethod.PUT, "/api/login").permitAll()
                    .requestMatchers("/api/prometheus/**").hasRole("MONITORING")
                    .anyRequest().authenticated())
            .httpBasic()
            .and()
            .oauth2ResourceServer(OAuth2ResourceServerConfigurer::jwt)
            .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
            .exceptionHandling(exceptions -> exceptions
                    .authenticationEntryPoint(new BearerTokenAuthenticationEntryPoint())
                    .accessDeniedHandler(new BearerTokenAccessDeniedHandler()))
            .build();
}

3. 排查自定义过滤器/拦截器

检查项目中是否存在自定义Filter或Interceptor,确认是否有修改请求路径的逻辑,避免将/api/login重写为/api/api/login。

4. 验证返回值序列化

确保LoginUserOutput是普通POJO类,Spring能正常将其序列化为JSON,避免因序列化异常触发二次请求。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.22 09:15:41