【发布时间】:2021-05-27 16:15:50
【问题描述】:
我有一个受 spring security oauth2 保护的 springboot webflux 应用程序。我在应用程序中有受限制和不受限制的端点。将授权标头传递给不受限制的端点时,应用程序抛出 401。当我不为不受限制的端点传递授权标头时,它工作正常。我可以看到 AuthenticationManager 正在通过授权标头时为受限和非受限端点执行。
SecurityWebFilterChain bean 配置如下。
public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity serverHttpSecurity) {
return serverHttpSecurity
.requestCache()
.requestCache(NoOpServerRequestCache.getInstance())
.and()
.securityContextRepository(NoOpServerSecurityContextRepository.getInstance())
.exceptionHandling()
.authenticationEntryPoint((swe, e) -> Mono.fromRunnable(() -> swe.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED)))
.accessDeniedHandler((swe, e) -> Mono.fromRunnable(() -> swe.getResponse().setStatusCode(HttpStatus.FORBIDDEN)))
.and().csrf().disable()
.authorizeExchange()
.pathMatchers("/api/unrestricted").permitAll()
.and()
.authorizeExchange().anyExchange().authenticated()
.and()
.oauth2ResourceServer()
.jwt(jwtSpec -> jwtSpec.authenticationManager(authenticationManager()))
.authenticationEntryPoint((swe, e) -> Mono.fromRunnable(() -> swe.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED)))
.accessDeniedHandler((swe, e) -> Mono.fromRunnable(() -> swe.getResponse().setStatusCode(HttpStatus.FORBIDDEN)))
.and().build();
}
AuthenticationManager 代码如下。
private ReactiveAuthenticationManager authenticationManager() {
return authentication -> {
log.info("executing authentication manager");
return Mono.justOrEmpty(authentication)
.filter(auth -> auth instanceof BearerTokenAuthenticationToken)
.cast(BearerTokenAuthenticationToken.class)
.filter(token -> RSAHelper.verifySigning(token.getToken()))
.switchIfEmpty(Mono.error(new BadCredentialsException("Invalid token")))
.map(token -> (Authentication) new UsernamePasswordAuthenticationToken(
token.getToken(),
token.getToken(),
Collections.emptyList()
));
};
}
当我们的一位 API 使用者为不受限制的端点发送虚拟授权标头时,我们发现了这个问题。
我可以在 SpringMVC Oauth2 找到类似问题的 Spring MVC 解决方案。
我在 github 项目demo-security 中有一个工作示例。我写了几个集成测试来解释这个问题。
@AutoConfigureWebTestClient
@SpringBootTest
public class DemoIT {
@Autowired
private WebTestClient webTestClient;
@Test
void testUnrestrictedEndpointWithAuthorizationHeader() {
webTestClient.get()
.uri("/api/unrestricted")
.header(HttpHeaders.AUTHORIZATION, "Bearer token") // fails when passing token
.exchange()
.expectStatus().isOk();
}
@Test
void testUnrestrictedEndpoint() {
webTestClient.get()
.uri("/api/unrestricted")
.exchange()
.expectStatus().isOk();
}
@SneakyThrows
@Test
void testRestrictedEndpoint() {
webTestClient.get()
.uri("/api/restricted")
.header(HttpHeaders.AUTHORIZATION, "Bearer " + RSAHelper.getJWSToken())
.exchange()
.expectStatus().isOk();
}
}
我不确定可能是什么问题。我的安全配置是否配置错误?任何帮助将不胜感激。
【问题讨论】:
-
你传递了一个无效的令牌,所以它会失败。某些东西不受限制的事实并不意味着安全标头不会被解析......
-
你有
.anyExchange()我怀疑这会覆盖你之前的声明 -
@Toerktumlare 我尝试了不同的设置,但没有帮助
-
@M.Deinum 我同意。我在这里要说明的一点是为什么 authenticationManager 正在为不受限制的端点执行
-
.authorizeExchange().anyExchange().authenticated()你调用authorizeExchange两次,这意味着你正在覆盖前一个。
标签: java spring-boot spring-webflux spring-security-oauth2