首页 > 解决方案 > 我需要获取身份验证令牌并将其设置在标题中

问题描述

我是 Spring Boot 和响应式编程的新手。

我正在将 spring webflux webclient 用于外部 api 服务。我需要获取身份验证令牌并将其设置在标题中

WebClient.builder()
            .baseUrl(baseUrl)
            .filter((request, next) -> {
                return next.exchange(request)
                        .flatMap((Function<ClientResponse, Mono<ClientResponse>>) clientResponse -> {
                            if (clientResponse.statusCode().value() == 401) {
                                return authenticate().map(token -> {
                                    Token accessToken = authenticate().block();
                                    ClientRequest retryRequest = ClientRequest.from(request).header("Authorisation", "Bearer " + accessToken.getAccessToken()).build();
                                    return next.exchange(retryRequest);
                                }).
                            } else {
                                return Mono.just(clientResponse);
                            }
                        });
            })
            .defaultHeader("Authorization", "Bearer " + authToken.getAccessToken())
            .build();


private Mono<Token> authenticate() {
    MultiValueMap<String, String> params = new LinkedMultiValueMap();
    params.add("client_id", clientId);
    params.add("client_secret", clientSecret);
    params.add("grant_type", "password");
    params.add("username", username);
    params.add("password", password);

    WebClient client = WebClient.create(baseUrl);
    return client
            .post()
            .uri(tokenUri)
            .accept(MediaType.APPLICATION_JSON)
            .contentType(MediaType.APPLICATION_FORM_URLENCODED)
            .syncBody(params)
            .retrieve()
            .bodyToMono(Token.class);
}

private static class Token {
    @JsonProperty("access_token")
    private String accessToken;

    public String getAccessToken() { return accessToken; }
}

在应用程序启动期间,我将获取访问令牌并将其设置在 webclient builder 中。我创建了一个过滤器来处理令牌到期后的身份验证失败。但是上面的代码会抛出错误,因为我使用了不应该在反应器线程中使用的 block()。我还能怎么处理?我正在使用 oauth2 资源所有者密码授予流程。有没有其他方法来处理流量?

标签: spring-bootspring-webflux

解决方案


嗨,我有同样的问题(添加重试 WebClient 的所有请求),看起来你已经重用了。但这flatmap是你的朋友,如果你有一个,Mono<Mono<T>>你可以用它来压平它flatMap

builder.baseUrl("http://localhost:8080")
             //sets the header before the exchange
            .filter(((request, next) -> tokenProvider.getAccessToken()
                                .map(setBearerTokenInHeader(request))
                                .flatMap(next::exchange)))
            //do the exchange
            .filter((request, next) -> next.exchange(request)
                    .flatMap(clientResponse -> {
                        if (clientResponse.statusCode().value() == 401) {
                          //If unauthenicated try again 
                            return authenticate()
                                    .flatMap(Token::getAccessToken)
                                    .map(setBearerTokenInHeader(request))
                                    .flatMap(next::exchange);
                        } else {
                            return Mono.just(clientResponse);
                        }
                    }))
            .build();

private Function<String, ClientRequest> setBearerTokenInHeader(ClientRequest request) {
        return token -> ClientRequest.from(request).header("Bearer ", token).build();
    }

推荐阅读