如何在Spring Boot中支持不同的Bearer token类型?

问题描述 投票:0回答:1

我们需要构建一个支持传统定制(非 JWT)承载令牌身份验证标头的服务:

Authorization: bespoke ....

和 JWT Bearer 令牌身份验证标头:

Authorization: Bearer ......

使用 Webflux 和 oauth2-resource-server 实现将 Bespoke + JWT 验证组合为 Spring Boot 3.2 中后备的 Bean 最简洁的方法是什么?

我期望一个自定义 bean 组成 DefaultBearerTokenResolver 和一个定制解析器,或者一个自定义 ReactiveAuthenticationManagerResolver bean 执行相同的操作,但我不确定哪个会更简单。

spring-boot spring-security jwt spring-webflux
1个回答
0
投票

如果两者之间的安全配置明显不同,您应该定义不同的

SecurityWebFilterChain
beans:

@Bean
@Order(Ordered.HIGHEST_PRECEDENCE)
SecurityWebFilterChain bespokeFilterChain(ServerHttpSecurity http) {
    http.securityMatcher((ServerWebExchange exchange) -> {
        final var isBespoke = Optional.ofNullable(exchange.getRequest().getHeaders().getFirst(HttpHeaders.AUTHORIZATION)).map(auth -> auth.toLowerCase().startsWith("bespoke ") ? true : false).orElse(false);
        return isBespoke ? ServerWebExchangeMatcher.MatchResult.match() : ServerWebExchangeMatcher.MatchResult.notMatch();
    });
    ...
}

@Bean
@Order(Ordered.HIGHEST_PRECEDENCE + 1)
SecurityWebFilterChain bearerFilterChain(ServerHttpSecurity http) {
    http.securityMatcher((ServerWebExchange exchange) -> {
        final var isBearer = Optional.ofNullable(exchange.getRequest().getHeaders().getFirst(HttpHeaders.AUTHORIZATION)).map(auth -> auth.toLowerCase().startsWith("bearer ") ? true : false).orElse(false);
        return isBearer ? ServerWebExchangeMatcher.MatchResult.match() : ServerWebExchangeMatcher.MatchResult.notMatch();
    });
    ...
}

...

@Bean
@Order(Ordered.LOWEST_PRECEDENCE)
SecurityWebFilterChain defaultFilterChain(ServerHttpSecurity http) {
    // No http.securityMatcher here
    // Processes requests which were not matched in filter chains with higher precedence (for instance anonymous requests)
    ...
}

这里我演示了

Authorization
标头上的匹配器,但您可以匹配请求中的任何内容(路径、媒体类型、源 IP 等)。

当不同身份验证策略之间的安全要求不同时,这非常有用,例如,当混合

oauth2Login()
(需要会话、CSRF 保护,并且通常配置
302 redirect to login
用于对受保护资源的未经授权的请求)和
oauth2ResourceServer()
时,这非常有用(通常是无状态的,
401 unauthorized
)在单个应用程序中。

如果除了身份验证管理器之外的所有内容都是相同的,那么您可能想要实现自己的

ReactiveAuthenticationManagerResolver<ServerWebExchange>

@Bean
SecurityWebFilterChain securityFilterChain(ServerHttpSecurity http, ReactiveAuthenticationManagerResolver<ServerWebExchange> authenticationManagerResolver) {
    http.oauth2ResourceServer(oauth2 -> oauth2.authenticationManagerResolver(authenticationManagerResolver));
    ...
}

@Component
static class MyReactiveAuthenticationManagerResolver implements ReactiveAuthenticationManagerResolver<ServerWebExchange> {
    private final ReactiveAuthenticationManager bespokeAuthManager;
    private final ReactiveAuthenticationManager bearerAuthManager;
    private final ReactiveAuthenticationManager defaultAuthManager;
    
    MyReactiveAuthenticationManagerResolver() {
        ...
    }

    @Override
    public Mono<ReactiveAuthenticationManager> resolve(ServerWebExchange context) {
        final var authHeader = Optional.ofNullable(context.getRequest().getHeaders().getFirst(HttpHeaders.AUTHORIZATION)).map(auth -> auth.toLowerCase()).orElse("");
        if(authHeader.startsWith("bespoke ")) {
            return Mono.just(bespokeAuthManager);
        }
        if(authHeader.startsWith("bearer ")) {
            return Mono.just(bearerAuthManager);
        }
        return Mono.just(defaultAuthManager);
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.