401- 使用 REST API Dynamics CRM 与来自 Spring 启动应用程序的 Azure AD 的未经授权身份验证
401- Unauthorized authentication using REST API Dynamics CRM with Azure AD from a Spring Boot app
我有一个 Spring 启动应用程序,它成功地从 Azure 身份验证端点获取访问令牌。但是当我尝试使用相同的令牌访问 Dynamics CRM Online REST API 时,我收到了 - 401 Unauthorized: [no body]
这就是我获取访问令牌的方式
@Bean(name = "oAuth2RestTemplate")
public OAuth2RestTemplate oAuth2RestTemplate() {
String tokenUri = "https://login.windows.net/<tenant id>/oauth2/token";
ClientCredentialsResourceDetails resourceDetails = new ClientCredentialsResourceDetails();
resourceDetails.setId("1");
resourceDetails.setAccessTokenUri(tokenUri);
resourceDetails.setClientId(clientid);
resourceDetails.setClientSecret(clientsecret);
resourceDetails.setGrantType("client_credentials");
resourceDetails.setScope(Arrays.asList("read", "write"));
AccessTokenRequest atr = new DefaultAccessTokenRequest();
atr.add("resource", "https://my.api.crm9.dynamics.com");
OAuth2ClientContext clientContext = new DefaultOAuth2ClientContext(atr);
OAuth2RestTemplate oauth2RestTemplate = new OAuth2RestTemplate(resourceDetails, clientContext);
return oauth2RestTemplate;
}
这是我使用此 OAuth2RestTemplate 获得的访问令牌 -
{"access_token":"eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6ImtnMkxZczJUMENUaklmajRydDZKSXluZW4zOCIsImtpZCI6ImtnMkxZczJUMENUaklmajRydDZKSXluZW4zOCJ9.eyJhdWQiOiIwMDAwMDAwMi0wMDAwLTAwMDAtYzAwMC0wMDAwMDAwMDAwMDAiLCJpc3MiOiJodHRwczovL3N0cy53aW5kb3dzLm5ldC82MDQwOTE1Yi05ZGZmLTRkNDctYmIzNS04YWM5YzlhNWRjMTgvIiwiaWF0IjoxNjAyNjcxNzYyLCJuYmYiOjE2MDI2NzE3NjIsImV4cCI6MTYwMjY3NTY2MiwiYWlvIjoiRTJSZ1lQRGlWbHRZZlYxNHc5TTMxMmFmQ3BUWEFnQT0iLCJhcHBpZCI6IjM2NDlmNTUxLTRlMjEtNDY3OS04YThjLTA4ZDhmNmIwOTA3MSIsImFwcGlkYWNyIjoiMSIsImlkcCI6Imh0dHBzOi8vc3RzLndpbmRvd3MubmV0LzYwNDA5MTViLTlkZmYtNGQ0Ny1iYjM1LThhYzljOWE1ZGMxOC8iLCJvaWQiOiJiM2FmOTcxYi1lMDQ5LTQ1ZDktOTViNy1hMTg3ZjQ4MWQzNWYiLCJyaCI6IjAuQVMwQVc1RkFZUC1kUjAyN05Zckp5YVhjR0ZIMVNUWWhUbmxHaW93STJQYXdrSEV0QUFBLiIsInN1YiI6ImIzYWY5NzFiLWUwNDktNDVkOS05NWI3LWExODdmNDgxZDM1ZiIsInRlbmFudF9yZWdpb25fc2NvcGUiOiJOQSIsInRpZCI6IjYwNDA5MTViLTlkZmYtNGQ0Ny1","token_type":"Bearer","expires_in":3528,"ext_expires_in":"3599","expires_on":"1602675662","not_before":"1602671762","resource":"**00000002-0000-0000-c000-000000000000**"}
如您所见,访问令牌中的资源与我想要的资源端点不同 - https://my.api.crm9.dynamics.com
我正在尝试以这种方式访问我的休息服务 -
public List<Contact> findContacts() throws MalformedURLException, IOException {
System.out.println("getting access token");
final UriComponentsBuilder uriComponentsBuilder = UriComponentsBuilder.fromHttpUrl(
"https://my.api.crm9.dynamics.com").path("/api/data/v9.1/contacts?$select=_accountid,address_stateorprovince," +
"contactid,emailaddress,fullname&$filter=startswith(emailaddress,'something')");
final String url = uriComponentsBuilder.build().toUriString();
System.out.println("url is :: "+url);
try {
ResponseEntity<List<Contact>> response = oAuth2RestTemplate.exchange(url, HttpMethod.GET, createRequestEntity(),
new ParameterizedTypeReference<List<Contact>>() {});
List<Contact> contacts = null;
if (null != response) {
contacts = response.getBody();
}
return contacts;
} catch (final Exception e) {
log.error(e.getMessage());
log.error("<<<<<<<<<<<<<<<<<<<<<<<<<<>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
log.error("Failed to retrieve contacts from Dynamics CRM.", e);
return new ArrayList<>();
}
我看过OAuth2.0 authentication of Dynamics CRM WebAPIs from a background Java (Spring) application
资源(网络 api 基础 url)正在 DefaultAccessTokenRequest 中设置,但这似乎对我的情况没有任何影响。
我的理论是访问令牌中的资源不同导致在尝试访问其余端点时出现 401。但我似乎无法明确设置资源。我的问题 -
- 我的 401 还有其他理论吗?
- 如何在我的访问令牌中明确设置我的资源?我在创建 OAuth2RestTemplate
时尝试在 DefaultAccessTokenRequest 中执行此操作
如果对其他人有帮助,我会回答我自己的问题。
我的问题中的方法获取了令牌,但没有像我提到的那样在令牌上设置资源 属性。因此休息端点身份验证失败。
以下方法对我有用,可以获取具有所需资源的令牌 -
public String getToken() throws MalformedURLException, IOException {
// create headers
final HttpHeaders headers = new HttpHeaders();
// set `content-type` header
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
// set `accept` header
headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
// request body parameters
final MultiValueMap<String, String> map = new LinkedMultiValueMap<String, String>();
map.add("grant_type", "client_credentials");
map.add("resource", "https://my.api.crm9.dynamics.com");
map.add("client_id", clientid);
map.add("client_secret", clientsecret);
// build the request
final HttpEntity<MultiValueMap<String, String>> entity = new HttpEntity<>(map, headers);
// send POST request
final ResponseEntity<String> response = restTemplate.postForEntity(
dynamicsOAuth2ClientProperties.getAccessTokenUri(), entity, String.class);
//Extract the token from the json response
final ObjectMapper mapper = new ObjectMapper();
final JsonNode actualObj = mapper.readTree(response.getBody());
return actualObj.get("access_token").asText();
}
我有一个 Spring 启动应用程序,它成功地从 Azure 身份验证端点获取访问令牌。但是当我尝试使用相同的令牌访问 Dynamics CRM Online REST API 时,我收到了 - 401 Unauthorized: [no body]
这就是我获取访问令牌的方式
@Bean(name = "oAuth2RestTemplate")
public OAuth2RestTemplate oAuth2RestTemplate() {
String tokenUri = "https://login.windows.net/<tenant id>/oauth2/token";
ClientCredentialsResourceDetails resourceDetails = new ClientCredentialsResourceDetails();
resourceDetails.setId("1");
resourceDetails.setAccessTokenUri(tokenUri);
resourceDetails.setClientId(clientid);
resourceDetails.setClientSecret(clientsecret);
resourceDetails.setGrantType("client_credentials");
resourceDetails.setScope(Arrays.asList("read", "write"));
AccessTokenRequest atr = new DefaultAccessTokenRequest();
atr.add("resource", "https://my.api.crm9.dynamics.com");
OAuth2ClientContext clientContext = new DefaultOAuth2ClientContext(atr);
OAuth2RestTemplate oauth2RestTemplate = new OAuth2RestTemplate(resourceDetails, clientContext);
return oauth2RestTemplate;
}
这是我使用此 OAuth2RestTemplate 获得的访问令牌 -
{"access_token":"eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6ImtnMkxZczJUMENUaklmajRydDZKSXluZW4zOCIsImtpZCI6ImtnMkxZczJUMENUaklmajRydDZKSXluZW4zOCJ9.eyJhdWQiOiIwMDAwMDAwMi0wMDAwLTAwMDAtYzAwMC0wMDAwMDAwMDAwMDAiLCJpc3MiOiJodHRwczovL3N0cy53aW5kb3dzLm5ldC82MDQwOTE1Yi05ZGZmLTRkNDctYmIzNS04YWM5YzlhNWRjMTgvIiwiaWF0IjoxNjAyNjcxNzYyLCJuYmYiOjE2MDI2NzE3NjIsImV4cCI6MTYwMjY3NTY2MiwiYWlvIjoiRTJSZ1lQRGlWbHRZZlYxNHc5TTMxMmFmQ3BUWEFnQT0iLCJhcHBpZCI6IjM2NDlmNTUxLTRlMjEtNDY3OS04YThjLTA4ZDhmNmIwOTA3MSIsImFwcGlkYWNyIjoiMSIsImlkcCI6Imh0dHBzOi8vc3RzLndpbmRvd3MubmV0LzYwNDA5MTViLTlkZmYtNGQ0Ny1iYjM1LThhYzljOWE1ZGMxOC8iLCJvaWQiOiJiM2FmOTcxYi1lMDQ5LTQ1ZDktOTViNy1hMTg3ZjQ4MWQzNWYiLCJyaCI6IjAuQVMwQVc1RkFZUC1kUjAyN05Zckp5YVhjR0ZIMVNUWWhUbmxHaW93STJQYXdrSEV0QUFBLiIsInN1YiI6ImIzYWY5NzFiLWUwNDktNDVkOS05NWI3LWExODdmNDgxZDM1ZiIsInRlbmFudF9yZWdpb25fc2NvcGUiOiJOQSIsInRpZCI6IjYwNDA5MTViLTlkZmYtNGQ0Ny1","token_type":"Bearer","expires_in":3528,"ext_expires_in":"3599","expires_on":"1602675662","not_before":"1602671762","resource":"**00000002-0000-0000-c000-000000000000**"}
如您所见,访问令牌中的资源与我想要的资源端点不同 - https://my.api.crm9.dynamics.com
我正在尝试以这种方式访问我的休息服务 -
public List<Contact> findContacts() throws MalformedURLException, IOException {
System.out.println("getting access token");
final UriComponentsBuilder uriComponentsBuilder = UriComponentsBuilder.fromHttpUrl(
"https://my.api.crm9.dynamics.com").path("/api/data/v9.1/contacts?$select=_accountid,address_stateorprovince," +
"contactid,emailaddress,fullname&$filter=startswith(emailaddress,'something')");
final String url = uriComponentsBuilder.build().toUriString();
System.out.println("url is :: "+url);
try {
ResponseEntity<List<Contact>> response = oAuth2RestTemplate.exchange(url, HttpMethod.GET, createRequestEntity(),
new ParameterizedTypeReference<List<Contact>>() {});
List<Contact> contacts = null;
if (null != response) {
contacts = response.getBody();
}
return contacts;
} catch (final Exception e) {
log.error(e.getMessage());
log.error("<<<<<<<<<<<<<<<<<<<<<<<<<<>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
log.error("Failed to retrieve contacts from Dynamics CRM.", e);
return new ArrayList<>();
}
我看过OAuth2.0 authentication of Dynamics CRM WebAPIs from a background Java (Spring) application 资源(网络 api 基础 url)正在 DefaultAccessTokenRequest 中设置,但这似乎对我的情况没有任何影响。
我的理论是访问令牌中的资源不同导致在尝试访问其余端点时出现 401。但我似乎无法明确设置资源。我的问题 -
- 我的 401 还有其他理论吗?
- 如何在我的访问令牌中明确设置我的资源?我在创建 OAuth2RestTemplate 时尝试在 DefaultAccessTokenRequest 中执行此操作
如果对其他人有帮助,我会回答我自己的问题。 我的问题中的方法获取了令牌,但没有像我提到的那样在令牌上设置资源 属性。因此休息端点身份验证失败。
以下方法对我有用,可以获取具有所需资源的令牌 -
public String getToken() throws MalformedURLException, IOException {
// create headers
final HttpHeaders headers = new HttpHeaders();
// set `content-type` header
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
// set `accept` header
headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
// request body parameters
final MultiValueMap<String, String> map = new LinkedMultiValueMap<String, String>();
map.add("grant_type", "client_credentials");
map.add("resource", "https://my.api.crm9.dynamics.com");
map.add("client_id", clientid);
map.add("client_secret", clientsecret);
// build the request
final HttpEntity<MultiValueMap<String, String>> entity = new HttpEntity<>(map, headers);
// send POST request
final ResponseEntity<String> response = restTemplate.postForEntity(
dynamicsOAuth2ClientProperties.getAccessTokenUri(), entity, String.class);
//Extract the token from the json response
final ObjectMapper mapper = new ObjectMapper();
final JsonNode actualObj = mapper.readTree(response.getBody());
return actualObj.get("access_token").asText();
}