问题描述
我有两条路径/foo
和/bar
。对于/foo
,我有一个自定义的身份验证机制。在/bar
上,我进行了Basic
认证。
除一种情况外,此设置均可正常运行。如果我没有在AUTHORIZATION
中传递/foo
标头,则将启动基本身份验证,而不是MyAuthenticationFailureHandler
是否可以为给定路径设置MyAuthenticationFailureHandler
?还是我用SecurityWebFiltersOrder
弄乱了东西?
我的完整安全性配置:
bean<MyReactiveUserDetailsService>()
bean<MyReactiveAuthenticationManager>()
bean {
ref<ServerHttpSecurity>()
.securityMatcher {
if (it.request.path.value().contains("/bar")) {
ServerWebExchangeMatcher.MatchResult.match()
} else {
ServerWebExchangeMatcher.MatchResult.notMatch()
}
}
.formLogin().disable()
.csrf().disable()
.logout().disable()
.httpBasic()
.and()
.authorizeExchange()
.pathMatchers("/bar/**")
.hasRole("ADMIN")
.anyExchange().permitAll()
.and()
.build()
}
bean {
ref<ServerHttpSecurity>()
.securityMatcher {
if (it.request.path.value().contains("/bar")) {
ServerWebExchangeMatcher.MatchResult.notMatch()
} else {
ServerWebExchangeMatcher.MatchResult.match()
}
}
.httpBasic().disable()
.formLogin().disable()
.csrf().disable()
.logout().disable()
.authorizeExchange()
.pathMatchers(
HttpMethod.POST,"/foo/**"
).hasRole(
"ABRACADABRA"
)
.anyExchange().permitAll()
.and()
.addFilterAt(
authenticationWebFilter(ref(),ref()),SecurityWebFiltersOrder.AUTHENTICATION
)
.build()
}
}
private fun authenticationWebFilter(
reactiveAuthenticationManager: ReactiveAuthenticationManager,objectMapper: ObjectMapper
) =
AuthenticationWebFilter(reactiveAuthenticationManager).apply {
setServerAuthenticationConverter(MyAuthenticationConverter())
setRequiresAuthenticationMatcher(
ServerWebExchangeMatchers.pathMatchers(
HttpMethod.POST,"/foo/**"
)
)
setAuthenticationFailureHandler(MyAuthenticationFailureHandler(objectMapper))
}
class MyAuthenticationConverter : ServerAuthenticationConverter {
override fun convert(exchange: ServerWebExchange): Mono<Authentication> {
val authHeader: String? = exchange.request.headers.getFirst(HttpHeaders.AUTHORIZATION)
// ...
return when {
isValid(authHeader,...) -> {
Mono.just(
UsernamePasswordAuthenticationToken(principal,credentials)
)
}
else -> Mono.empty()
}
}
}
class MyAuthenticationFailureHandler(private val objectMapper: ObjectMapper) : ServerAuthenticationFailureHandler {
override fun onAuthenticationFailure(
webFilterExchange: WebFilterExchange,exception: AuthenticationException?
): Mono<Void> {
val response = webFilterExchange.exchange.response
response.apply {
statusCode = HttpStatus.OK
headers.contentType = MediaType.APPLICATION_JSON
headers.set(HttpHeaders.WARNING,"""199 warning "Invalid token"""")
}
return response.writeWith(
Flux.just(
DefaultDataBufferFactory().wrap(
objectMapper.writeValueAsBytes(
MyDto(
// ...
).toSettingResponse()
)
)
)
)
}
}
解决方法
您可以使用 AuthenticationWebFilter
设置自定义 ServerAuthenticationFailureHandler
。
@Bean
public SecurityWebFilterChain securitygWebFilterChain(ServerHttpSecurity http) {
AuthenticationWebFilter authenticationWebFilter = new AuthenticationWebFilter(authenticationManager);
authenticationWebFilter.setAuthenticationFailureHandler(myAuthenticationFailureHandler)
return http.authorizeExchange()
.pathMatchers("/user/signup")
.permitAll().pathMatchers("/user/login")
.permitAll()
.anyExchange().authenticated()
.and()
.securityContextRepository(NoOpServerSecurityContextRepository.getInstance())
.addFilterAt(authenticationWebFilter,SecurityWebFiltersOrder.AUTHENTICATION)
.httpBasic().disable()
.csrf().disable()
.formLogin().disable()
.logout().disable()
.build();
}