java 如何在ReactiveSecurityContextHolder中访问Spring Webflux中的JWT

xu3bshqb  于 2023-10-14  发布在  Java
关注(0)|答案(1)|浏览(135)

我如何访问JTW令牌?.block()抛出异常,所有其他方法似乎都不起作用。
以下是我通常是如何做的:

@Bean("CurrentUser")
public Supplier<JwtAuthenticationToken> currentUser() {
    return () -> (JwtAuthenticationToken) SecurityContextHolder.getContext().getAuthentication();
}

然后我把它传入构造函数:

@Qualifier("CurrentUser") Supplier<JwtAuthenticationToken> currentUserSupplier

这里有一个用例:

currentUserSupplier.get().getTokenAttributes().get(USER_MAIN_ID_TOKEN)

我试图用 ReactiveSecurityContextHolder 做同样的事情,但事实证明这是不可能的。
我已经试过这些问题的解答了。它们都返回nothing或null。
ReactiveSecurityContextHolder is empty in Spring WebFlux
How to get claims from jwt token in spring webflux
block()/blockFirst()/blockLast() are blocking error when calling bodyToMono AFTER exchange()
How to get String from Mono in reactive java
也许这是根本不可能得到它在一个“阻塞的方式”,因为它是一个React式的框架。有没有人给予进一步澄清?

ecbunoof

ecbunoof1#

好吧,基本上我让它做我想做的,但在React的方式。只是需要重构一堆代码。我想我需要学会如何以被动的方式思考。
然而,如果有人需要一种方法来获得索赔在一个非React的方式,这里是一个黑客的方式,我发现。假设你控制着调用你的服务的客户端,只需要在请求中发送你想要访问的声明。当然,这是不好的,因为有人可以发送虚假信息。但是要解决这个问题,你所要做的就是将它们与你的jwt令牌进行比较(以一种React式的方式XD)。在安全过滤器链中添加一个将进行此验证的自定义过滤器。下面是一个实现:

  • 在这个例子中,我想从令牌中获取值“mainId”,所以我只是在路径变量中发送它,然后在安全过滤器中只为x路径我检查路径变量中发送的值是否与令牌中的值相同,如果是,我知道我可以使用它,如果不是,请求不正确,我返回403*

运行筛选器的decompression路径:

private final ServerWebExchangeMatcher pathMatcher = ServerWebExchangeMatchers.pathMatchers("/notification/getEvents/**");

安全过滤器链:

@Bean
public SecurityWebFilterChain filterChain(ServerHttpSecurity http) throws Exception {
    http.csrf().disable()
                .authorizeExchange(authorize -> {
                try {
                    authorize.anyExchange().authenticated()
                            .and()
                            .addFilterAt(this::filter, SecurityWebFiltersOrder.AUTHORIZATION);;
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
            })
            .oauth2ResourceServer(
                    oauth2 -> oauth2.authenticationManagerResolver(getJwtIssuerAuthenticationManagerResolver()));

    http.headers()
            .xssProtection()
            .disable()
            .frameOptions();

    return http.build();

您的过滤器:

public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
    return pathMatcher.matches(exchange)
            .flatMap(matchResult -> {
                if (matchResult.isMatch()) {
                    return checkClaim(exchange);
                } else {
                    return chain.filter(exchange);
                }
            });
}

查看声明:

private Mono<Void> checkClaim(ServerWebExchange exchange) {

        return ReactiveSecurityContextHolder.getContext()
                .map(SecurityContext::getAuthentication)
                .flatMap(authentication -> {
                    if (authentication instanceof JwtAuthenticationToken) {
                        JwtAuthenticationToken jwtAuthenticationToken = (JwtAuthenticationToken) authentication;
                        String pathVariableMainId = getMainIdFromExchange(exchange);
                        if (jwtAuthenticationToken.getTokenAttributes().containsKey("mainId")) {
                            String claimMainId = jwtAuthenticationToken.getTokenAttributes().get("mainId").toString();
                            if (pathVariableMainId.equals(claimMainId)) {
                                return Mono.empty(); // Authorized
                            }
                        }
                    }
                    return Mono.error(new AccessDeniedException("Access is denied"));
                });
    }

从path变量中获取主ID:

public String getMainIdFromExchange(ServerWebExchange exchange ){
    String path = exchange.getRequest().getPath().value();
    UriTemplate uriTemplate = new UriTemplate("/notification/getEvents/{mainId}");
    Map<String, String> parameters = new HashMap<>();
    parameters = uriTemplate.match(path); //extract values form template

    return parameters.get("mainId");
}

我知道这是非常hacky,但它的工作。:)
如果有人知道更好的方法,请添加一个响应:)

相关问题