英文:
401- Unauthorized authentication using REST API Dynamics CRM with Azure AD from a Spring Boot app
问题
我有一个Spring Boot应用程序,可以成功从Azure身份验证终结点获取访问令牌。但是当我尝试使用相同的令牌访问Dynamics CRM在线REST API时,我得到了- 401 未经授权:[没有正文]
这是我获取访问令牌的方式:
@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.eyJhdWQiOiIwMDAwMDAwMi0wMDAwLTAwMDAtYzAwMC0wMDAwMDAwMDAwMDAiLCJpc3MiOiJodHRwczovL3N0cy53aW5kb3dzLm5ldC82MDQwOTE1Yi05ZGZmLTRkNDctYmIzNS04YWM5YzlhNWRjMTgvIiwiaWF0IjoxNjAyNjcxNzYyLCJuYmYiOjE2MDI2NzE3NjIsImV4cCI6MTYwMjY3NTY2MiwiYWlvIjoiRTJSZ1lQRGlWbHRZZlYxNHc5TTMxMmFmQ3BUWEFnQT0iLCJhcHBpZCI6IjM2NDlmNTUxLTRlMjEtNDY3OS04YThjLTA4ZDhmNmIwOTA3MSIsImFwcGlkYWNyIjoiMSIsImlkcCI6Imh0dHBzOi8vc3RzLndpbmRvd3MubmV0LzYwNDA5
正如您所看到的,访问令牌中的资源与我期望的资源终结点 - https://my.api.crm9.dynamics.com 不同。
我尝试通过以下方式访问我的REST服务 -
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<>();
}
}
我查看了 https://stackoverflow.com/questions/47391447/oauth2-0-authentication-of-dynamics-crm-webapis-from-a-background-java-spring
资源(Web API基本URL)在DefaultAccessTokenRequest中进行了设置,但在我的情况下似乎没有任何影响。
我的理论是访问令牌中的资源不同,这会在尝试访问REST终结点时导致401错误。但是我似乎无法显式设置资源。我的问题 -
- 关于我的401错误还有其他理论吗?
- 我如何在我的访问令牌中显式设置资源?我在创建OAuth2RestTemplate时已经尝试过在DefaultAccessTokenRequest中进行设置。
英文:
I have a Spring Boot application that gets access token from the Azure authentication endpoint successfully. But when I try to use the same token to access Dynamics CRM Online REST API, I am getting - 401 Unauthorized: [no body]
This is how I get my access token
@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;
}
This is what I get as my accesstoken using this 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**"}
As you can see, the resource in the access token is different than my desired resource endpoint - https://my.api.crm9.dynamics.com
I am trying to access my rest service this way -
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<>();
}
I have looked at https://stackoverflow.com/questions/47391447/oauth2-0-authentication-of-dynamics-crm-webapis-from-a-background-java-spring
Resource(the web api base url) is being set in the DefaultAccessTokenRequest but that does not seem to have any impact in my case.
My theory is the resource in the access token being different is causing a 401 while trying to access the rest endpoint. But I cannot seem to be able to set the resource explicitly. My questions -
- Any other theories for my 401?
- How can I explicitly set my resource in my access token? I have tried doing it in my DefaultAccessTokenRequest while creating my OAuth2RestTemplate
答案1
得分: 1
我会在这里提供你要翻译的代码部分:
public String getToken() throws MalformedURLException, IOException {
// 创建头部信息
final HttpHeaders headers = new HttpHeaders();
// 设置 `content-type` 头部
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
// 设置 `accept` 头部
headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
// 请求体参数
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);
// 构建请求
final HttpEntity<MultiValueMap<String, String>> entity = new HttpEntity<>(map, headers);
// 发送 POST 请求
final ResponseEntity<String> response = restTemplate.postForEntity(
dynamicsOAuth2ClientProperties.getAccessTokenUri(), entity, String.class);
// 从 JSON 响应中提取令牌
final ObjectMapper mapper = new ObjectMapper();
final JsonNode actualObj = mapper.readTree(response.getBody());
return actualObj.get("access_token").asText();
}
英文:
I will answer my own question in case its helpful for someone else.
My method in my question get the token but did not set the resource property on the token as I had mentioned. Consequently rest endpoint authentication failed.
The following method worked for me to get the token with required resource -
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();
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论