升级Spring Boot 3后PATCH接口出现404问题求助
问题描述
将Kotlin API迁移到Spring Boot 3后,出现部分接口无法访问的情况:
- GET请求
localhost:8081/search/hello可正常访问,对应端点代码:
@PreAuthorize("hasRole('ROLE_MYROLE')") @GetMapping("/search/{somephrase}") suspend fun getSomething(@PathVariable("somephrase") phrase: String): someResponse { return myService.getPhrase(phrase) }
- PATCH请求
localhost:8081/update/resourceId返回404,对应端点代码:
@PreAuthorize("hasRole('ROLE_MYROLE')") @PatchMapping("/update/{resourceId}") suspend fun updateSomething( @PathVariable("resourceId") resourceId: Long, @RequestBody updateJson: JsonNode ): UpdateResponse { return recommendationService.update(updateJson, resourceId) }
收到的404响应:
{ "timestamp": "2023-02-12 09:59:50", "status": 404, "error": "Not Found", "message": "No message available", "path": "/search" }
控制台日志片段:
: [preHandle] PATCH /error - operationId: 00000000000000000000000000000000 |
当前Spring Security配置:
import org.springframework.context.annotation.Bean import org.springframework.context.annotation.Configuration import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity import org.springframework.security.config.annotation.web.builders.HttpSecurity import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity import org.springframework.security.web.DefaultSecurityFilterChain @Configuration @EnableWebSecurity class ResourceServerConfiguration { @Bean fun configure(http: HttpSecurity): DefaultSecurityFilterChain? { http.authorizeHttpRequests() .requestMatchers("/csrf").permitAll() .requestMatchers("/**").authenticated() .and() .csrf() .disable() .oauth2ResourceServer().jwt().jwtAuthenticationConverter { AuthAwareTokenConverter().convert(it) } return http.build() } }
Token转换器代码(用于扩展令牌声明):
import org.springframework.core.convert.converter.Converter import org.springframework.security.authentication.AbstractAuthenticationToken import org.springframework.security.core.GrantedAuthority import org.springframework.security.core.authority.SimpleGrantedAuthority import org.springframework.security.oauth2.jwt.Jwt import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken import org.springframework.security.oauth2.server.resource.authentication.JwtGrantedAuthoritiesConverter class AuthAwareTokenConverter : Converter<Jwt, AbstractAuthenticationToken> { private val jwtGrantedAuthoritiesConverter: Converter<Jwt, Collection<GrantedAuthority>> = JwtGrantedAuthoritiesConverter() override fun convert(jwt: Jwt): AbstractAuthenticationToken { val claims = jwt.claims val principal = findPrincipal(claims) val authorities = extractAuthorities(jwt) return AuthAwareAuthenticationToken(jwt, principal, authorities) } private fun findPrincipal(claims: Map<String, Any?>): String { return if (claims.containsKey(CLAIM_USERNAME)) { claims[CLAIM_USERNAME] as String } else if (claims.containsKey(CLAIM_USER_ID)) { claims[CLAIM_USER_ID] as String } else { claims[CLAIM_CLIENT_ID] as String } } private fun extractAuthorities(jwt: Jwt): Collection<GrantedAuthority> { val authorities = mutableListOf<GrantedAuthority>().apply { addAll(jwtGrantedAuthoritiesConverter.convert(jwt)!!) } if (jwt.claims.containsKey(CLAIM_AUTHORITY)) { @Suppress("UNCHECKED_CAST") val claimAuthorities = (jwt.claims[CLAIM_AUTHORITY] as Collection<String>).toList() authorities.addAll(claimAuthorities.map(::SimpleGrantedAuthority)) } return authorities.toSet() } companion object { const val CLAIM_USERNAME = "user_name" const val CLAIM_USER_ID = "user_id" const val CLAIM_CLIENT_ID = "client_id" const val CLAIM_AUTHORITY = "authorities" } } class AuthAwareAuthenticationToken( jwt: Jwt, private val aPrincipal: String, authorities: Collection<GrantedAuthority> ) : JwtAuthenticationToken(jwt, authorities) { override fun getPrincipal(): String { return aPrincipal } }
已尝试简化PATCH端点(仅返回字符串),问题依旧,推测根源在Spring Security配置。
排查与解决方案
1. 定位请求路径异常
响应中path字段显示为/search,但实际请求的是/update/resourceId,说明请求可能被过滤器/拦截器修改了路径,或者存在错误的转发规则。需检查项目中自定义的过滤器、拦截器是否干扰了请求路径的传递。
2. 修复JWT转换器的实例化问题
当前配置中每次调用转换器都新建AuthAwareTokenConverter实例,可能导致Spring上下文传递异常。修改配置为注入单例Bean:
@Configuration @EnableWebSecurity class ResourceServerConfiguration { @Bean fun authAwareTokenConverter(): AuthAwareTokenConverter { return AuthAwareTokenConverter() } @Bean fun configure(http: HttpSecurity): DefaultSecurityFilterChain { http.authorizeHttpRequests() .requestMatchers("/csrf").permitAll() .anyRequest().authenticated() .and() .csrf().disable() .oauth2ResourceServer() .jwt() .jwtAuthenticationConverter(authAwareTokenConverter()) return http.build() } }
3. 验证协程与Spring Boot 3兼容性
由于端点使用suspend函数,需确保协程依赖配置正确:
- 确认引入
kotlinx-coroutines-spring依赖 - 检查控制器类是否添加
@RestController注解,确保协程端点被正确扫描
4. 启用调试日志追踪请求流转
添加日志配置,打印Security过滤器链的详细执行过程:
logging.level.org.springframework.security=DEBUG logging.level.org.springframework.web=DEBUG
通过日志查看请求在过滤器链中的每一步流转,确认是否在某个环节被拦截或路径被篡改。
5. 检查HTTP方法支持
Spring Boot 3对HTTP方法的默认处理无变更,但需确保:
- 控制器类上的路径前缀(如
@RequestMapping)未覆盖@PatchMapping的路径 - 没有其他Security配置类(如遗留的
WebSecurityConfigurerAdapter)与当前配置冲突
内容的提问来源于stack exchange,提问作者Johnny Alpha
相关产品推荐
相关产品推荐

