无法使用 RestTemplate 连接 Spring-Integration
Unable to wire Spring-Integration with RestTemplate
我完全是 Spring 集成框架的菜鸟。
我正在尝试使用使用 OAuth2 的 REST API。我正在使用 Spring 集成 xml-based 配置。
我的问题是似乎无法正确连接网关和 Rest 模板以发送令牌请求的 body (multi-part)
这在我的 spring 集成配置文件中:
spring-integration-context.xml
<!-- Rest Template -->
<bean id="oAuth2RestTemplate"
class="org.springframework.security.oauth2.client.OAuth2RestTemplate">
<constructor-arg ref="clientCredentialsResource"/>
</bean>
<!-- Used by Rest Template -->
<bean id= "clientCredentialsResource"
class="org.springframework.security.oauth2.client.token.grant.client.ClientCredentialsResourceDetails">
<property name="clientId" value="${oauth2.clientId}" />
<property name="clientSecret" value="${oauth2.clientSecret}" />
<property name="accessTokenUri" value="${oauth2.accessTokenUri}" />
</bean>
<!-- Channels for requesting token -->
<int:channel id="tokenRequestChannel"/>
<int:channel id="tokenResponseChannel"/>
<!-- Gateway for requesting token -->
<int-http:outbound-gateway id="authRequestGateway"
request-channel="tokenRequestChannel"
url="${oauth2.endPointUri}"
reply-timeout="30000"
http-method="GET"
rest-template="oAuth2RestTemplate"
reply-channel="tokenResponseChannel"
charset="UTF-8"
expected-response-type="java.lang.String">
</int-http:outbound-gateway>
要获得初始令牌,我知道我需要 POST 我在 header 中的凭据类似于:
Method: POST
Authorization: Basic <base64-encoded clientId:clientSecret>
Content-Type: application/x-www-form-urlencoded
并在 body
中添加 multi-part(表格)
grant_type=client_credentials&scope=read
API 要求字符串 grant_type=client_credentials&scope=read
在请求的 body(不是 URL)中,因为这是一个 POST
(不是GET
).
我已经尝试了一些东西(太多了 remember/recount 全部)但是 我不确定 where/how 将负载放入我的请求中。
我遗漏了一些东西(很明显?),我现在不知道是什么。
这是我在日志中得到的 (request/response):
2016-11-10 16:46:22.429 DEBUG 6384 --- [ask-scheduler-3]
s.n.www.protocol.http.HttpURLConnection :
sun.net.www.MessageHeader@2d1f9cc810 pairs: {POST
[redacted]/oauth/token HTTP/1.1: null}{Authorization: Basic
[redacted]}{Accept:
application/json, application/x-www-form-urlencoded}{Content-Type:
application/x-www-form-urlencoded}{Cache-Control: no-cache}{Pragma:
no-cache}{User-Agent: Java/1.8.0_72}{Host:
[redacted]}{Connection: keep-alive}{Content-Length: 29}
2016-11-10 16:46:22.584 DEBUG 6384 --- [ask-scheduler-3]
s.n.www.protocol.http.HttpURLConnection :
sun.net.www.MessageHeader@29200db310 pairs: {null: HTTP/1.1 400 Bad
Request}{Cache-Control: no-cache}{Pragma: no-cache}{Content-Type:
application/json; charset=utf-8}{Expires: -1}{Server:
[redacted]}{[redacted]}{[redacted]}{Date: Fri, 11 Nov 2016 00:46:48 GMT}{Content-Length: 46}
2016-11-10 16:46:22.586 ERROR 6384 --- [ask-scheduler-3]
o.s.integration.handler.LoggingHandler :
org.springframework.messaging.MessageHandlingException: HTTP request
execution failed for URI
[redacted];
nested exception is error="access_denied", error_description="Access
token denied." at
org.springframework.integration.http.outbound.HttpRequestExecutingMessageHandler.handleRequestMessage(HttpRequestExecutingMessageHandler.java:409)
at
org.springframework.integration.handler.AbstractReplyProducingMessageHandler.handleMessageInternal(AbstractReplyProducingMessageHandler.java:109)
at
org.springframework.integration.handler.AbstractMessageHandler.handleMessage(AbstractMessageHandler.java:127)
at
org.springframework.integration.dispatcher.AbstractDispatcher.tryOptimizedDispatch(AbstractDispatcher.java:116)
at
org.springframework.integration.dispatcher.UnicastingDispatcher.doDispatch(UnicastingDispatcher.java:148)
at
org.springframework.integration.dispatcher.UnicastingDispatcher.dispatch(UnicastingDispatcher.java:121)
at
org.springframework.integration.channel.AbstractSubscribableChannel.doSend(AbstractSubscribableChannel.java:77)
at
org.springframework.integration.channel.AbstractMessageChannel.send(AbstractMessageChannel.java:423)
at
org.springframework.integration.channel.AbstractMessageChannel.send(AbstractMessageChannel.java:373)
at
org.springframework.messaging.core.GenericMessagingTemplate.doSend(GenericMessagingTemplate.java:115)
at
org.springframework.messaging.core.GenericMessagingTemplate.doSend(GenericMessagingTemplate.java:45)
at
org.springframework.messaging.core.AbstractMessageSendingTemplate.send(AbstractMessageSendingTemplate.java:105)
at
org.springframework.integration.endpoint.SourcePollingChannelAdapter.handleMessage(SourcePollingChannelAdapter.java:195)
at
org.springframework.integration.endpoint.AbstractPollingEndpoint.doPoll(AbstractPollingEndpoint.java:272)
at
org.springframework.integration.endpoint.AbstractPollingEndpoint.access[=20=]0(AbstractPollingEndpoint.java:58)
at
org.springframework.integration.endpoint.AbstractPollingEndpoint.call(AbstractPollingEndpoint.java:190)
at
org.springframework.integration.endpoint.AbstractPollingEndpoint.call(AbstractPollingEndpoint.java:186)
at
org.springframework.integration.endpoint.AbstractPollingEndpoint$Poller.run(AbstractPollingEndpoint.java:353)
at
org.springframework.integration.util.ErrorHandlingTaskExecutor.run(ErrorHandlingTaskExecutor.java:55)
at
org.springframework.core.task.SyncTaskExecutor.execute(SyncTaskExecutor.java:50)
at
org.springframework.integration.util.ErrorHandlingTaskExecutor.execute(ErrorHandlingTaskExecutor.java:51)
at
org.springframework.integration.endpoint.AbstractPollingEndpoint$Poller.run(AbstractPollingEndpoint.java:344)
at
org.springframework.scheduling.support.DelegatingErrorHandlingRunnable.run(DelegatingErrorHandlingRunnable.java:54)
at
org.springframework.scheduling.concurrent.ReschedulingRunnable.run(ReschedulingRunnable.java:81)
at
java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
at java.util.concurrent.FutureTask.run(FutureTask.java:266) at
java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access1(ScheduledThreadPoolExecutor.java:180)
at
java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:293)
at
java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at
java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:745) Caused by:
error="access_denied", error_description="Access token denied." at
org.springframework.security.oauth2.client.token.OAuth2AccessTokenSupport.retrieveToken(OAuth2AccessTokenSupport.java:142)
at
org.springframework.security.oauth2.client.token.grant.client.ClientCredentialsAccessTokenProvider.obtainAccessToken(ClientCredentialsAccessTokenProvider.java:44)
at
org.springframework.security.oauth2.client.token.AccessTokenProviderChain.obtainNewAccessTokenInternal(AccessTokenProviderChain.java:142)
at
org.springframework.security.oauth2.client.token.AccessTokenProviderChain.obtainAccessToken(AccessTokenProviderChain.java:118)
at
org.springframework.security.oauth2.client.OAuth2RestTemplate.acquireAccessToken(OAuth2RestTemplate.java:221)
at
org.springframework.security.oauth2.client.OAuth2RestTemplate.getAccessToken(OAuth2RestTemplate.java:173)
at
org.springframework.security.oauth2.client.OAuth2RestTemplate.createRequest(OAuth2RestTemplate.java:105) at
org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:615)
at
org.springframework.security.oauth2.client.OAuth2RestTemplate.doExecute(OAuth2RestTemplate.java:128)
at
org.springframework.web.client.RestTemplate.execute(RestTemplate.java:595)
at
org.springframework.web.client.RestTemplate.exchange(RestTemplate.java:516)
at
org.springframework.integration.http.outbound.HttpRequestExecutingMessageHandler.handleRequestMessage(HttpRequestExecutingMessageHandler.java:382)
... 30 more Caused by: error="invalid_request",
error_description="OAuth Error", message="{ "error": "invalid_scope"
}" at
org.springframework.security.oauth2.common.exceptions.OAuth2ExceptionJackson2Deserializer.deserialize(OAuth2ExceptionJackson2Deserializer.java:120)
at
org.springframework.security.oauth2.common.exceptions.OAuth2ExceptionJackson2Deserializer.deserialize(OAuth2ExceptionJackson2Deserializer.java:33)
at
com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:3789)
at
com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:2913)
at
org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.readJavaType(AbstractJackson2HttpMessageConverter.java:225)
at
org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.readInternal(AbstractJackson2HttpMessageConverter.java:205)
at
org.springframework.http.converter.AbstractHttpMessageConverter.read(AbstractHttpMessageConverter.java:193)
at
org.springframework.security.oauth2.client.token.OAuth2AccessTokenSupport$AccessTokenErrorHandler.handleError(OAuth2AccessTokenSupport.java:235)
at
org.springframework.web.client.RestTemplate.handleResponse(RestTemplate.java:667)
at
org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:620)
at
org.springframework.web.client.RestTemplate.execute(RestTemplate.java:588)
at
org.springframework.security.oauth2.client.token.OAuth2AccessTokenSupport.retrieveToken(OAuth2AccessTokenSupport.java:137)
... 41 more
因此,据我所知,我收到一条 "Access Denied" 消息,因为我无法在请求的 body 中设置范围。
注1
此外,我已经查看了示例项目中的代码,但没有看到我的问题的答案。
注2
我已验证(通过 POSTMAN)凭据、端点 (URI)、范围等都是正确的。
更新 1
根据 @Artem-Bilan 的评论,我将方法更改为 POST
并添加了下面的代码以在请求中包含 body。
<!-- Add Payload -->
<int:inbound-channel-adapter id="oauth2ChannelAdapter" channel="tokenRequestChannel"
ref="grantAndScope" method="getGrantTypeAndScope">
<!-- Triggering requests every 5 seconds -->
<int:poller fixed-delay="5000" />
</int:inbound-channel-adapter>
<!-- Bean with Grant & Scope Payload -->
<bean id="grantAndScope" class="com.AuthTypeAndScopeInfo"/>
AuthTypeAndScopeInfo.java
public class AuthTypeAndScopeInfo {
/* adds grant type and scope as body to message */
public MultiValueMap<String, String> getGrantTypeAndScope() {
// Create the request body as a MultiValueMap
MultiValueMap<String, String> body = new LinkedMultiValueMap<String, String>();
body.add("grant_type", "client_credentials");
body.add("scope", "read");
return body;
}
}
然而,结果是一样的:/
所以,您真的必须提供 POST
而不是当前的 http-method="GET"
并将消息发送到带有 payload
的网关作为所需的 Map
对。
最后,我没有使用Oauth2RestTemplate
。
相反,我使用了下面的代码。代码基本上从创建一个 Map
有效载荷开始(正如 Artem 在他的回答中所建议的那样)并为第一个请求注入 headers。
然后它使用第一个请求的响应(包含访问令牌)将获得的令牌注入所有后续请求的header。
这里可能还有一些优化的空间,但就目前而言,这足以满足我的需求。
AuthTypeAndScopeInfo.java
public class AuthTypeAndScopeInfo {
/* adds grant type and scope as body to message */
public MultiValueMap<String, String> getGrantTypeAndScope() {
// Create the request body as a MultiValueMap
MultiValueMap<String, String> body = new LinkedMultiValueMap<String, String>();
body.add("grant_type", "client_credentials");
body.add("scope", "read");
return body;
}
}
spring-integration-context.xml
<!-- Add Payload -->
<int:inbound-channel-adapter id="oauth2ChannelAdapter" channel="preTokenRequestChannel"
ref="grantAndScope" method="getGrantTypeAndScope">
<!-- Triggering requests every 5 seconds -->
<int:poller fixed-delay="5000" />
</int:inbound-channel-adapter>
<!-- POJO with Grant & Scope Payload -->
<bean id="grantAndScope" class="com.AuthTypeAndScopeInfo"/>
<!-- Adding headers for Token Request -->
<int:header-enricher input-channel="preTokenRequestChannel"
output-channel="tokenRequestChannel">
<int:header name="Authorization" value="Basic <clientId:clientSecret>"/>
<int:header name="Content-Type" value="application/x-www-form-urlencoded"/>
</int:header-enricher>
<!-- Channels for requesting token -->
<int:channel id="tokenRequestChannel"/>
<int:channel id="tokenResponseChannel"/>
<!-- Channels for Authenticated requests (with valid token) -->
<int:channel id="authenticatedRequestChannel"/>
<int:channel id="authenticatedResponseChannel"/>
<!-- Gateway for requesting token -->
<!-- REST request to authorization server for a token -->
<!-- replies time out after 30 seconds -->
<int-http:outbound-gateway id="tokenRequestGateway"
request-channel="tokenRequestChannel"
url="${security.oauth2.client.accessTokenUri}"
reply-timeout="30000"
http-method="POST"
reply-channel="tokenResponseChannel"
charset="UTF-8"
expected-response-type="java.lang.String">
</int-http:outbound-gateway>
<!-- Adding headers for Authenticated Request (contains newly obtained token) -->
<int:chain input-channel="tokenResponseChannel"
output-channel="authenticatedRequestChannel">
<int:header-enricher>
<!-- Adds token_type and the actual (authenticated)
access_token to the header of the next request
(overwriting the Basic <...> previous entry) -->
<int:header name="Authorization" overwrite="true"
expression="#jsonPath(payload,'$.token_type') + ' ' + #jsonPath(payload,'$.access_token')" />
</int:header-enricher>
</int:chain>
<!-- REST request with pre-authorized token -->
<!-- replies time out after 30 seconds -->
<int-http:outbound-gateway id="authenticatedRequestGateway"
request-channel="authenticatedRequestChannel"
url="${security.oauth2.client.endPointUri}"
reply-timeout="30000"
http-method="GET"
reply-channel="authenticatedResponseChannel"
charset="UTF-8"
expected-response-type="java.lang.String">
</int-http:outbound-gateway>
我完全是 Spring 集成框架的菜鸟。
我正在尝试使用使用 OAuth2 的 REST API。我正在使用 Spring 集成 xml-based 配置。
我的问题是似乎无法正确连接网关和 Rest 模板以发送令牌请求的 body (multi-part)
这在我的 spring 集成配置文件中:
spring-integration-context.xml
<!-- Rest Template -->
<bean id="oAuth2RestTemplate"
class="org.springframework.security.oauth2.client.OAuth2RestTemplate">
<constructor-arg ref="clientCredentialsResource"/>
</bean>
<!-- Used by Rest Template -->
<bean id= "clientCredentialsResource"
class="org.springframework.security.oauth2.client.token.grant.client.ClientCredentialsResourceDetails">
<property name="clientId" value="${oauth2.clientId}" />
<property name="clientSecret" value="${oauth2.clientSecret}" />
<property name="accessTokenUri" value="${oauth2.accessTokenUri}" />
</bean>
<!-- Channels for requesting token -->
<int:channel id="tokenRequestChannel"/>
<int:channel id="tokenResponseChannel"/>
<!-- Gateway for requesting token -->
<int-http:outbound-gateway id="authRequestGateway"
request-channel="tokenRequestChannel"
url="${oauth2.endPointUri}"
reply-timeout="30000"
http-method="GET"
rest-template="oAuth2RestTemplate"
reply-channel="tokenResponseChannel"
charset="UTF-8"
expected-response-type="java.lang.String">
</int-http:outbound-gateway>
要获得初始令牌,我知道我需要 POST 我在 header 中的凭据类似于:
Method: POST
Authorization: Basic <base64-encoded clientId:clientSecret>
Content-Type: application/x-www-form-urlencoded
并在 body
中添加 multi-part(表格)grant_type=client_credentials&scope=read
API 要求字符串 grant_type=client_credentials&scope=read
在请求的 body(不是 URL)中,因为这是一个 POST
(不是GET
).
我已经尝试了一些东西(太多了 remember/recount 全部)但是 我不确定 where/how 将负载放入我的请求中。
我遗漏了一些东西(很明显?),我现在不知道是什么。
这是我在日志中得到的 (request/response):
2016-11-10 16:46:22.429 DEBUG 6384 --- [ask-scheduler-3] s.n.www.protocol.http.HttpURLConnection : sun.net.www.MessageHeader@2d1f9cc810 pairs: {POST [redacted]/oauth/token HTTP/1.1: null}{Authorization: Basic [redacted]}{Accept: application/json, application/x-www-form-urlencoded}{Content-Type: application/x-www-form-urlencoded}{Cache-Control: no-cache}{Pragma: no-cache}{User-Agent: Java/1.8.0_72}{Host: [redacted]}{Connection: keep-alive}{Content-Length: 29} 2016-11-10 16:46:22.584 DEBUG 6384 --- [ask-scheduler-3] s.n.www.protocol.http.HttpURLConnection : sun.net.www.MessageHeader@29200db310 pairs: {null: HTTP/1.1 400 Bad Request}{Cache-Control: no-cache}{Pragma: no-cache}{Content-Type: application/json; charset=utf-8}{Expires: -1}{Server: [redacted]}{[redacted]}{[redacted]}{Date: Fri, 11 Nov 2016 00:46:48 GMT}{Content-Length: 46} 2016-11-10 16:46:22.586 ERROR 6384 --- [ask-scheduler-3] o.s.integration.handler.LoggingHandler : org.springframework.messaging.MessageHandlingException: HTTP request execution failed for URI [redacted]; nested exception is error="access_denied", error_description="Access token denied." at org.springframework.integration.http.outbound.HttpRequestExecutingMessageHandler.handleRequestMessage(HttpRequestExecutingMessageHandler.java:409) at org.springframework.integration.handler.AbstractReplyProducingMessageHandler.handleMessageInternal(AbstractReplyProducingMessageHandler.java:109) at org.springframework.integration.handler.AbstractMessageHandler.handleMessage(AbstractMessageHandler.java:127) at org.springframework.integration.dispatcher.AbstractDispatcher.tryOptimizedDispatch(AbstractDispatcher.java:116) at org.springframework.integration.dispatcher.UnicastingDispatcher.doDispatch(UnicastingDispatcher.java:148) at org.springframework.integration.dispatcher.UnicastingDispatcher.dispatch(UnicastingDispatcher.java:121) at org.springframework.integration.channel.AbstractSubscribableChannel.doSend(AbstractSubscribableChannel.java:77) at org.springframework.integration.channel.AbstractMessageChannel.send(AbstractMessageChannel.java:423) at org.springframework.integration.channel.AbstractMessageChannel.send(AbstractMessageChannel.java:373) at org.springframework.messaging.core.GenericMessagingTemplate.doSend(GenericMessagingTemplate.java:115) at org.springframework.messaging.core.GenericMessagingTemplate.doSend(GenericMessagingTemplate.java:45) at org.springframework.messaging.core.AbstractMessageSendingTemplate.send(AbstractMessageSendingTemplate.java:105) at org.springframework.integration.endpoint.SourcePollingChannelAdapter.handleMessage(SourcePollingChannelAdapter.java:195) at org.springframework.integration.endpoint.AbstractPollingEndpoint.doPoll(AbstractPollingEndpoint.java:272) at org.springframework.integration.endpoint.AbstractPollingEndpoint.access[=20=]0(AbstractPollingEndpoint.java:58) at org.springframework.integration.endpoint.AbstractPollingEndpoint.call(AbstractPollingEndpoint.java:190) at org.springframework.integration.endpoint.AbstractPollingEndpoint.call(AbstractPollingEndpoint.java:186) at org.springframework.integration.endpoint.AbstractPollingEndpoint$Poller.run(AbstractPollingEndpoint.java:353) at org.springframework.integration.util.ErrorHandlingTaskExecutor.run(ErrorHandlingTaskExecutor.java:55) at org.springframework.core.task.SyncTaskExecutor.execute(SyncTaskExecutor.java:50) at org.springframework.integration.util.ErrorHandlingTaskExecutor.execute(ErrorHandlingTaskExecutor.java:51) at org.springframework.integration.endpoint.AbstractPollingEndpoint$Poller.run(AbstractPollingEndpoint.java:344) at org.springframework.scheduling.support.DelegatingErrorHandlingRunnable.run(DelegatingErrorHandlingRunnable.java:54) at org.springframework.scheduling.concurrent.ReschedulingRunnable.run(ReschedulingRunnable.java:81) at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511) at java.util.concurrent.FutureTask.run(FutureTask.java:266) at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access1(ScheduledThreadPoolExecutor.java:180) at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:293) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) at java.lang.Thread.run(Thread.java:745) Caused by: error="access_denied", error_description="Access token denied." at org.springframework.security.oauth2.client.token.OAuth2AccessTokenSupport.retrieveToken(OAuth2AccessTokenSupport.java:142) at org.springframework.security.oauth2.client.token.grant.client.ClientCredentialsAccessTokenProvider.obtainAccessToken(ClientCredentialsAccessTokenProvider.java:44) at org.springframework.security.oauth2.client.token.AccessTokenProviderChain.obtainNewAccessTokenInternal(AccessTokenProviderChain.java:142) at org.springframework.security.oauth2.client.token.AccessTokenProviderChain.obtainAccessToken(AccessTokenProviderChain.java:118) at org.springframework.security.oauth2.client.OAuth2RestTemplate.acquireAccessToken(OAuth2RestTemplate.java:221) at org.springframework.security.oauth2.client.OAuth2RestTemplate.getAccessToken(OAuth2RestTemplate.java:173) at org.springframework.security.oauth2.client.OAuth2RestTemplate.createRequest(OAuth2RestTemplate.java:105) at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:615) at org.springframework.security.oauth2.client.OAuth2RestTemplate.doExecute(OAuth2RestTemplate.java:128) at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:595) at org.springframework.web.client.RestTemplate.exchange(RestTemplate.java:516) at org.springframework.integration.http.outbound.HttpRequestExecutingMessageHandler.handleRequestMessage(HttpRequestExecutingMessageHandler.java:382) ... 30 more Caused by: error="invalid_request", error_description="OAuth Error", message="{ "error": "invalid_scope" }" at org.springframework.security.oauth2.common.exceptions.OAuth2ExceptionJackson2Deserializer.deserialize(OAuth2ExceptionJackson2Deserializer.java:120) at org.springframework.security.oauth2.common.exceptions.OAuth2ExceptionJackson2Deserializer.deserialize(OAuth2ExceptionJackson2Deserializer.java:33) at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:3789) at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:2913) at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.readJavaType(AbstractJackson2HttpMessageConverter.java:225) at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.readInternal(AbstractJackson2HttpMessageConverter.java:205) at org.springframework.http.converter.AbstractHttpMessageConverter.read(AbstractHttpMessageConverter.java:193) at org.springframework.security.oauth2.client.token.OAuth2AccessTokenSupport$AccessTokenErrorHandler.handleError(OAuth2AccessTokenSupport.java:235) at org.springframework.web.client.RestTemplate.handleResponse(RestTemplate.java:667) at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:620) at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:588) at org.springframework.security.oauth2.client.token.OAuth2AccessTokenSupport.retrieveToken(OAuth2AccessTokenSupport.java:137) ... 41 more
因此,据我所知,我收到一条 "Access Denied" 消息,因为我无法在请求的 body 中设置范围。
注1
此外,我已经查看了示例项目中的代码,但没有看到我的问题的答案。
注2
我已验证(通过 POSTMAN)凭据、端点 (URI)、范围等都是正确的。
更新 1
根据 @Artem-Bilan 的评论,我将方法更改为 POST
并添加了下面的代码以在请求中包含 body。
<!-- Add Payload -->
<int:inbound-channel-adapter id="oauth2ChannelAdapter" channel="tokenRequestChannel"
ref="grantAndScope" method="getGrantTypeAndScope">
<!-- Triggering requests every 5 seconds -->
<int:poller fixed-delay="5000" />
</int:inbound-channel-adapter>
<!-- Bean with Grant & Scope Payload -->
<bean id="grantAndScope" class="com.AuthTypeAndScopeInfo"/>
AuthTypeAndScopeInfo.java
public class AuthTypeAndScopeInfo {
/* adds grant type and scope as body to message */
public MultiValueMap<String, String> getGrantTypeAndScope() {
// Create the request body as a MultiValueMap
MultiValueMap<String, String> body = new LinkedMultiValueMap<String, String>();
body.add("grant_type", "client_credentials");
body.add("scope", "read");
return body;
}
}
然而,结果是一样的:/
所以,您真的必须提供 POST
而不是当前的 http-method="GET"
并将消息发送到带有 payload
的网关作为所需的 Map
对。
最后,我没有使用Oauth2RestTemplate
。
相反,我使用了下面的代码。代码基本上从创建一个 Map
有效载荷开始(正如 Artem 在他的回答中所建议的那样)并为第一个请求注入 headers。
然后它使用第一个请求的响应(包含访问令牌)将获得的令牌注入所有后续请求的header。
这里可能还有一些优化的空间,但就目前而言,这足以满足我的需求。
AuthTypeAndScopeInfo.java
public class AuthTypeAndScopeInfo {
/* adds grant type and scope as body to message */
public MultiValueMap<String, String> getGrantTypeAndScope() {
// Create the request body as a MultiValueMap
MultiValueMap<String, String> body = new LinkedMultiValueMap<String, String>();
body.add("grant_type", "client_credentials");
body.add("scope", "read");
return body;
}
}
spring-integration-context.xml
<!-- Add Payload -->
<int:inbound-channel-adapter id="oauth2ChannelAdapter" channel="preTokenRequestChannel"
ref="grantAndScope" method="getGrantTypeAndScope">
<!-- Triggering requests every 5 seconds -->
<int:poller fixed-delay="5000" />
</int:inbound-channel-adapter>
<!-- POJO with Grant & Scope Payload -->
<bean id="grantAndScope" class="com.AuthTypeAndScopeInfo"/>
<!-- Adding headers for Token Request -->
<int:header-enricher input-channel="preTokenRequestChannel"
output-channel="tokenRequestChannel">
<int:header name="Authorization" value="Basic <clientId:clientSecret>"/>
<int:header name="Content-Type" value="application/x-www-form-urlencoded"/>
</int:header-enricher>
<!-- Channels for requesting token -->
<int:channel id="tokenRequestChannel"/>
<int:channel id="tokenResponseChannel"/>
<!-- Channels for Authenticated requests (with valid token) -->
<int:channel id="authenticatedRequestChannel"/>
<int:channel id="authenticatedResponseChannel"/>
<!-- Gateway for requesting token -->
<!-- REST request to authorization server for a token -->
<!-- replies time out after 30 seconds -->
<int-http:outbound-gateway id="tokenRequestGateway"
request-channel="tokenRequestChannel"
url="${security.oauth2.client.accessTokenUri}"
reply-timeout="30000"
http-method="POST"
reply-channel="tokenResponseChannel"
charset="UTF-8"
expected-response-type="java.lang.String">
</int-http:outbound-gateway>
<!-- Adding headers for Authenticated Request (contains newly obtained token) -->
<int:chain input-channel="tokenResponseChannel"
output-channel="authenticatedRequestChannel">
<int:header-enricher>
<!-- Adds token_type and the actual (authenticated)
access_token to the header of the next request
(overwriting the Basic <...> previous entry) -->
<int:header name="Authorization" overwrite="true"
expression="#jsonPath(payload,'$.token_type') + ' ' + #jsonPath(payload,'$.access_token')" />
</int:header-enricher>
</int:chain>
<!-- REST request with pre-authorized token -->
<!-- replies time out after 30 seconds -->
<int-http:outbound-gateway id="authenticatedRequestGateway"
request-channel="authenticatedRequestChannel"
url="${security.oauth2.client.endPointUri}"
reply-timeout="30000"
http-method="GET"
reply-channel="authenticatedResponseChannel"
charset="UTF-8"
expected-response-type="java.lang.String">
</int-http:outbound-gateway>