我是Spring Integration Framework的完整菜鸟。
我正在尝试使用使用OAuth2的REST API。我正在使用基于Spring Integration xml的配置。
我的问题是似乎无法正确连接网关和Rest模板以发送令牌请求的正文(多部分)
这是我的spring集成配置文件:
弹簧整合-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>
要获取初始令牌,我知道我需要在标题中发布我的凭据,类似于:
Method: POST
Authorization: Basic <base64-encoded clientId:clientSecret>
Content-Type: application/x-www-form-urlencoded
并向身体添加多部分(形式)
grant_type=client_credentials&scope=read
API要求字符串grant_type=client_credentials&scope=read
位于请求的正文(而不是网址)中,因为这是POST
(不是GET
)。
我尝试了一些事情(太多要记住/重新计算所有内容),但是我不确定在何处/如何将有效负载放入我的请求中。
我遗失了一些东西(很明显?)而且我现在还不知道它是什么。
这是我在日志中获得的内容(请求/响应):
2016-11-10 16:46:22.429 DEBUG 6384 --- [ask-scheduler-3] s.n.www.protocol.http.HttpURLConnection: sun.net.www.MessageHeader@2d1f9cc810对:{POST [编辑] / oauth / token HTTP / 1.1:null} {授权:基本 [删除]} {接受: 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} {主持人: [编辑]} {连接:保持活着} {内容长度:29} 2016-11-10 16:46:22.584 DEBUG 6384 --- [ask-scheduler-3] s.n.www.protocol.http.HttpURLConnection: sun.net.www.MessageHeader@29200db310对:{null:HTTP / 1.1 400 Bad 请求} {Cache-Control:no-cache} {Pragma:no-cache} {Content-Type: 应用/ JSON; charset = utf-8} {Expires:-1} {Server: [编辑]} {[编辑]} {[编辑]} {日期:星期五,2016年11月11日00:46:48 GMT} {内容长度:46} 2016-11-10 16:46:22.586 ERROR 6384 --- [ask-scheduler-3] o.s.integration.handler.LoggingHandler: org.springframework.messaging.MessageHandlingException: HTTP请求 URI的执行失败 [删除]; 嵌套异常是错误=&#34; access_denied&#34;,error_description =&#34;访问 令牌被拒绝。&#34; at org.springframework.integration.http.outbound.HttpRequestExecutingMessageHandler.handleRequestMessage(HttpRequestExecutingMessageHandler.java:409) 在 org.springframework.integration.handler.AbstractReplyProducingMessageHandler.handleMessageInternal(AbstractReplyProducingMessageHandler.java:109) 在 org.springframework.integration.handler.AbstractMessageHandler.handleMessage(AbstractMessageHandler.java:127) 在 org.springframework.integration.dispatcher.AbstractDispatcher.tryOptimizedDispatch(AbstractDispatcher.java:116) 在 org.springframework.integration.dispatcher.UnicastingDispatcher.doDispatch(UnicastingDispatcher.java:148) 在 org.springframework.integration.dispatcher.UnicastingDispatcher.dispatch(UnicastingDispatcher.java:121) 在 org.springframework.integration.channel.AbstractSubscribableChannel.doSend(AbstractSubscribableChannel.java:77) 在 org.springframework.integration.channel.AbstractMessageChannel.send(AbstractMessageChannel.java:423) 在 org.springframework.integration.channel.AbstractMessageChannel.send(AbstractMessageChannel.java:373) 在 org.springframework.messaging.core.GenericMessagingTemplate.doSend(GenericMessagingTemplate.java:115) 在 org.springframework.messaging.core.GenericMessagingTemplate.doSend(GenericMessagingTemplate.java:45) 在 org.springframework.messaging.core.AbstractMessageSendingTemplate.send(AbstractMessageSendingTemplate.java:105) 在 org.springframework.integration.endpoint.SourcePollingChannelAdapter.handleMessage(SourcePollingChannelAdapter.java:195) 在 org.springframework.integration.endpoint.AbstractPollingEndpoint.doPoll(AbstractPollingEndpoint.java:272) 在 org.springframework.integration.endpoint.AbstractPollingEndpoint.access $ 000(AbstractPollingEndpoint.java:58) 在 org.springframework.integration.endpoint.AbstractPollingEndpoint $ 1.call(AbstractPollingEndpoint.java:190) 在 org.springframework.integration.endpoint.AbstractPollingEndpoint $ 1.call(AbstractPollingEndpoint.java:186) 在 org.springframework.integration.endpoint.AbstractPollingEndpoint $轮询$ 1.run(AbstractPollingEndpoint.java:353) 在 org.springframework.integration.util.ErrorHandlingTaskExecutor $ 1.run(ErrorHandlingTaskExecutor.java:55) 在 org.springframework.core.task.SyncTaskExecutor.execute(SyncTaskExecutor.java:50) 在 org.springframework.integration.util.ErrorHandlingTaskExecutor.execute(ErrorHandlingTaskExecutor.java:51) 在 org.springframework.integration.endpoint.AbstractPollingEndpoint $ Poller.run(AbstractPollingEndpoint.java:344) 在 org.springframework.scheduling.support.DelegatingErrorHandlingRunnable.run(DelegatingErrorHandlingRunnable.java:54) 在 org.springframework.scheduling.concurrent.ReschedulingRunnable.run(ReschedulingRunnable.java:81) 在 java.util.concurrent.Executors $ RunnableAdapter.call(Executors.java:511) 在java.util.concurrent.FutureTask.run(FutureTask.java:266)at java.util.concurrent.ScheduledThreadPoolExecutor中的$ ScheduledFutureTask.access $ 201(ScheduledThreadPoolExecutor.java:180) 在 java.util.concurrent.ScheduledThreadPoolExecutor中的$ ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:293) 在 java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) 在 java.util.concurrent.ThreadPoolExecutor中的$ Worker.run(ThreadPoolExecutor.java:617) 在java.lang.Thread.run(Thread.java:745)引起: 错误=&#34; access_denied&#34;,error_description =&#34;访问令牌被拒绝。&#34; at org.springframework.security.oauth2.client.token.OAuth2AccessTokenSupport.retrieveToken(OAuth2AccessTokenSupport.java:142) 在 org.springframework.security.oauth2.client.token.grant.client.ClientCredentialsAccessTokenProvider.obtainAccessToken(ClientCredentialsAccessTokenProvider.java:44) 在 org.springframework.security.oauth2.client.token.AccessTokenProviderChain.obtainNewAccessTokenInternal(AccessTokenProviderChain.java:142) 在 org.springframework.security.oauth2.client.token.AccessTokenProviderChain.obtainAccessToken(AccessTokenProviderChain.java:118) 在 org.springframework.security.oauth2.client.OAuth2RestTemplate.acquireAccessToken(OAuth2RestTemplate.java:221) 在 org.springframework.security.oauth2.client.OAuth2RestTemplate.getAccessToken(OAuth2RestTemplate.java:173) 在 org.springframework.security.oauth2.client.OAuth2RestTemplate.createRequest(OAuth2RestTemplate.java:105)at at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:615) 在 org.springframework.security.oauth2.client.OAuth2RestTemplate.doExecute(OAuth2RestTemplate.java:128) 在 org.springframework.web.client.RestTemplate.execute(RestTemplate.java:595) 在 org.springframework.web.client.RestTemplate.exchange(RestTemplate.java:516) 在 org.springframework.integration.http.outbound.HttpRequestExecutingMessageHandler.handleRequestMessage(HttpRequestExecutingMessageHandler.java:382) ... 30更多引起:错误=&#34; invalid_request&#34;, error_description =&#34; OAuth错误&#34;,message =&#34; {&#34;错误&#34;:&#34; invalid_scope&#34; }&#34; at org.springframework.security.oauth2.common.exceptions.OAuth2ExceptionJackson2Deserializer.deserialize(OAuth2ExceptionJackson2Deserializer.java:120) 在 org.springframework.security.oauth2.common.exceptions.OAuth2ExceptionJackson2Deserializer.deserialize(OAuth2ExceptionJackson2Deserializer.java:33) 在 com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:3789) 在 com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:2913) 在 org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.readJavaType(AbstractJackson2HttpMessageConverter.java:225) 在 org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.readInternal(AbstractJackson2HttpMessageConverter.java:205) 在 org.springframework.http.converter.AbstractHttpMessageConverter.read(AbstractHttpMessageConverter.java:193) 在 org.springframework.security.oauth2.client.token.OAuth2AccessTokenSupport $ AccessTokenErrorHandler.handleError(OAuth2AccessTokenSupport.java:235) 在 org.springframework.web.client.RestTemplate.handleResponse(RestTemplate.java:667) 在 org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:620) 在 org.springframework.web.client.RestTemplate.execute(RestTemplate.java:588) 在 org.springframework.security.oauth2.client.token.OAuth2AccessTokenSupport.retrieveToken(OAuth2AccessTokenSupport.java:137) ......还有41个
所以,从我所知道的,我得到了一个“拒绝访问”#34;消息,因为我无法在请求正文中设置范围。
注1
另外,我已经查看了示例项目中的代码,但没有看到我的问题的答案。
注2
我已经验证(通过POSTMAN)凭据,端点(URI),范围等是正确的。
更新1
根据@ Artem-Bilan的评论,我将方法更改为POST
并添加了以下代码,以便在请求中包含正文。
<!-- 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;
}
}
然而,结果是相同的:/
答案 0 :(得分:1)
因此,您真的要提供POST
而不是当前的http-method="GET"
,并将消息发送到该网关,payload
作为所需对的Map
。
答案 1 :(得分:1)
最后,我最终没有使用Oauth2RestTemplate
。
相反,我使用下面的代码。代码基本上是通过创建Map
有效负载(如Artem在他的回答中所建议的)开始的,并为第一个请求注入头。
然后它使用第一个请求(包含访问令牌)的响应将获取的令牌注入所有后续请求的标头中。
这里可能有一些优化空间,但就目前而言,这足以满足我的需求。
<强> 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;
}
}
<强>弹簧整合-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>