我通常会尽量避免使用 spring auth,因为它通常会注入许多极难禁用的默认值。但现在我需要使用 JWT 令牌。因此,我创建了一个安全配置,但发生了预身份验证失败,我无法弄清楚如何禁用:
Pre-authenticated entry point called. Rejecting access
任何人都可以与我分享如何在 boot 3 应用程序中禁用预身份验证吗?以下是我当前的配置
@Bean
@Throws(Exception::class)
fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
return http.csrf { it.disable() }
.authorizeHttpRequests { it.anyRequest().authenticated() }
.sessionManagement { it.sessionCreationPolicy(SessionCreationPolicy.STATELESS) }
.authenticationProvider(authenticationProvider())
.build()
}
@Bean
fun authenticationProvider(): AuthenticationProvider {
val authenticationProvider = JwtAuthenticationProvider(jwtDecoder())
return authenticationProvider
}
@Bean
fun jwtDecoder(): JwtDecoder {
return NimbusJwtDecoder.withPublicKey(rsaProperties.publicKey).build()
}
@Bean
fun jwtEncoder(): JwtEncoder {
val jwk: JWK =
RSAKey.Builder(rsaProperties.publicKey).privateKey(rsaProperties.privateKey).build()
val jwks: JWKSource<SecurityContext> = ImmutableJWKSet(JWKSet(jwk))
return NimbusJwtEncoder(jwks)
}
Spring Security Web 的调试日志:
2024-02-14T13:58:20.238-08:00 DEBUG 5308 --- [tp1468039653-29] o.s.security.web.FilterChainProxy: Securing POST /v1/ds/123456789/register
2024-02-14T13:58:20.239-08:00 DEBUG 5308 --- [tp1468039653-29] o.s.s.w.a.AnonymousAuthenticationFilter: Set SecurityContextHolder to anonymous SecurityContext
2024-02-14T13:58:20.239-08:00 DEBUG 5308 --- [tp1468039653-29] o.s.s.w.a.Http403ForbiddenEntryPoint : Pre-authenticated entry point called. Rejecting access
2024-02-14T13:58:20.243-08:00 DEBUG 5308 --- [tp1468039653-29] o.s.security.web.FilterChainProxy : Securing POST /error
2024-02-14T13:58:20.244-08:00 DEBUG 5308 --- [tp1468039653-29] o.s.s.w.a.AnonymousAuthenticationFilter : Set SecurityContextHolder to anonymous SecurityContext
2024-02-14T13:58:20.244-08:00 DEBUG 5308 --- [tp1468039653-29] o.s.s.w.a.Http403ForbiddenEntryPoint : Pre-authenticated entry point called. Rejecting access
所以问题是我相信因为 AuthenticationProvider 是一个 JwtAuthenticationProvider,jwt 安全过滤的核心将由该身份验证提供程序“提供”,但事实并非如此。我更新了我的安全配置以拥有 jwtFilter 和身份验证提供程序,现在它正在工作。为了解决一些循环引用,上述问题中的编码器/解码 bean 已移至单独的 bean 配置类。
@Configuration
@EnableWebSecurity
class SecurityConfig(
private val jwtDecoder: JwtDecoder,
private val jwtfilter: JwtAuthFilter
) {
@Bean
@Throws(Exception::class)
fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
return http.csrf { it.disable() }
.authorizeHttpRequests { it.anyRequest().authenticated() }
.sessionManagement { it.sessionCreationPolicy(SessionCreationPolicy.STATELESS) }
.authenticationProvider(authenticationProvider())
.addFilterBefore(jwtfilter, UsernamePasswordAuthenticationFilter::class.java)
.build()
}
@Bean
fun authenticationProvider(): AuthenticationProvider {
val authenticationProvider = JwtAuthenticationProvider(jwtDecoder)
return authenticationProvider
}
}
对于那些想要它的人来说,这里也是过滤器,过滤器中引用的 jwtService 只是使用 NimbusJwtDecoder 和 NimbusJwtEncoder 来完成所有繁重的工作:
@Component
class JwtAuthFilter(private val jwtService: JwtService) : OncePerRequestFilter() {
val log: Logger = LoggerFactory.getLogger(javaClass)
override fun doFilterInternal(
request: HttpServletRequest,
response: HttpServletResponse,
filterChain: FilterChain) {
val authHeader: String = request.getHeader("Authorization") ?: throw InvalidJwtException("No JWT Header provided")
var token: String? = null
var cachingRequest: ContentCachingRequestWrapper? = null
if (authHeader != null && authHeader.startsWith("Bearer ")) {
token = authHeader.removePrefix("Bearer").trim()
}
val contentType = request.contentType
if (contentType == null || !contentType.contains("multipart/form-data")) {
// Wrap the request, so we can read the payload multiple times
cachingRequest = ContentCachingRequestWrapper(request)
}
val payload = cachingRequest?.reader?.readText() ?: ""
if (SecurityContextHolder.getContext().authentication == null) {
log.trace("Validating JWT token")
jwtService.validateToken(token!!, payload)?.let {
val authenticationToken = JwtAuthenticationToken(it, listOf(SimpleGrantedAuthority("ROLE_USER")))
SecurityContextHolder.getContext().authentication = authenticationToken
} ?: throw InvalidJwtException("Invalid JWT token provided")
}
filterChain.doFilter(request, response)
}
}