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
相关产品推荐
相关产品推荐

