我有一个外部 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,它有效!这里有什么区别?
您在 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
,因为它们现在将位于标题中。