在 Spring 中使用 WebClient 收到 401

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

我有一个外部 API,我可以在 Postman 中获取身份验证令牌并 ping,而按照后续步骤操作不会出现问题:

    i have tested again in postman, works fine
    in Postman i use :
    
    Auth Type: OAuth 2.0
    
    Grant type: Client Credentials
    
    Access Token UR: the above
    
    Client ID:above
    
    Client Secret:above
    
    Scope: aboce
    
    Client Authentication: Send as Basic Auth headers
    
    i click "Get new token" proceed and all fine, 

然后我在 Spring 尝试这个方法

private OAuth2AuthorizedClient obtainToken(ClientRegistration clientRegistration) {
    String tokenUrl = clientRegistration.getProviderDetails().getTokenUri();
    String clientId = clientRegistration.getClientId();
    String clientSecret = clientRegistration.getClientSecret();
    String scope = "https://test.account.myconpamys/auth/gpr/.default"; // or your API's scope

    WebClient webClient = WebClient.builder().build();

    // Prepare the form data including client_id and client_secret
    MultiValueMap<String, String> formData = new LinkedMultiValueMap<>();
    formData.add("grant_type", "client_credentials");
    formData.add("client_id", clientId);
    formData.add("client_secret", clientSecret);
    formData.add("scope", scope);

    try {
        // Make the POST request
        Map<String, Object> responseBody = webClient.post()
                .uri(tokenUrl)
                .contentType(MediaType.APPLICATION_FORM_URLENCODED)
                .body(BodyInserters.fromFormData(formData))
                .retrieve()
                .bodyToMono(new ParameterizedTypeReference<Map<String, Object>>() {})
                .block();

        if (responseBody == null || !responseBody.containsKey("access_token")) {
            // Check if the response might be nested under a different key
            if (responseBody != null && responseBody.containsKey("token")) {
                responseBody = (Map<String, Object>) responseBody.get("token");
            }
            if (responseBody == null || !responseBody.containsKey("access_token")) {
                throw new OAuth2AuthenticationException(new OAuth2Error("invalid_token"), "Failed to obtain access token");
            }
        }

        // Extract the access token and other details
        String accessTokenValue = (String) responseBody.get("access_token");
        String tokenType = (String) responseBody.get("token_type");
        int expiresIn = (Integer) responseBody.get("expires_in");
        Instant issuedAt = Instant.now();
        Instant expiresAt = issuedAt.plusSeconds(expiresIn);

        OAuth2AccessToken accessToken = new OAuth2AccessToken(
                OAuth2AccessToken.TokenType.BEARER,
                accessTokenValue,
                issuedAt,
                expiresAt,
                clientRegistration.getScopes()
        );

        return new OAuth2AuthorizedClient(clientRegistration, "my-client", 
accessToken);
     } catch (Exception e) {
        // Log the exception, then throw an appropriate exception
        throw new OAuth2AuthenticationException(new OAuth2Error("invalid_token"), 
"Failed to obtain access token: " + e.getMessage(), e);
     }
 }

但我明白了

 checkpoint ⇢ 401 UNAUTHORIZED from POST 
 https://login.microsoftonline.com/myid/oauth2/v2.0/token [DefaultWebClient]
|_ Mono.switchIfEmpty ⇢ at 

但是为什么?我在 Postman 中使用相同的凭据,例如范围、clientId、secret、Auth Token url,它有效!这里有什么区别?

java spring webclient spring-webclient
1个回答
0
投票

您在 Postman 中使用

Send as Basic Auth headers
进行客户端身份验证,这意味着客户端凭据
client_id
client_secret
作为基本身份验证标头发送。但在您的 Web 客户端设置代码中,您在表单数据中发送
client_id
client_secret
,而不是作为基本身份验证标头。

在 WebClient 请求中添加基本身份验证标头:

WebClient webClient = WebClient.builder()
    .defaultHeaders(headers -> headers.setBasicAuth(clientId, clientSecret))
    .build();

然后从

client_id
中删除
client_secret
formData
,因为它们现在将位于标题中。

© www.soinside.com 2019 - 2024. All rights reserved.