OAuth2RestTemplate将字符集添加到内容类型标头中

时间:2019-04-06 00:30:11

标签: java spring spring-boot spring-security oauth

当尝试使用OAuth2RequestTemplate获取访问令牌时,调用会在请求中发送以下标头

"Content-Type", "application/x-www-form-urlencoded;charset=UTF-8”

我想删除不包含在其中的字符集。

我尝试通过AccessTokenRequest对象和CustomTokenProvider手动设置标头值,但没有用。

任何想法,为什么它实际上包含在其中以及如何摆脱它。

更新:包括代码示例

选项1:

String oauthServerUri = "..../access_token";

ClientCredentialsResourceDetails ccDetails = new ClientCredentialsResourceDetails();
ccDetails.setClientId("clientId");
ccDetails.setClientSecret("clientSecret");
ccDetails.setGrantType("client_credentials");
ccDetails.setAccessTokenUri(oauthServerUri);

AccessTokenRequest tokenRequest = new DefaultAccessTokenRequest();

Map<String, List<String>> headers = new HashMap<>();
headers.put("Content-Type", Arrays.asList("Some Proper Value"));
tokenRequest.setHeaders(headers);
OAuth2ClientContext context = new DefaultOAuth2ClientContext(tokenRequest);

OAuth2RestTemplate restTemplate = new OAuth2RestTemplate(ccDetails, context);
OAuth2AccessToken accessToken = restTemplate.getAccessToken();

选项2: 作为替代方案,我尝试了这里描述的方法: How to set HTTP Header for OAuth2RestTemplate 这正在实现AccessTokenProvider并在obtainAccessToken中设置标头。但这也没有帮助。

2 个答案:

答案 0 :(得分:0)

当您使构造函数成为新的OAuth2RestTemplate(ccDetails,context);在它的后面创建了一个super(),它创建了RestTemplate,因为它是从其扩展而来的。

public OAuth2RestTemplate(OAuth2ProtectedResourceDetails resource, OAuth2ClientContext context) {
        super();
        if (resource == null) {
            throw new IllegalArgumentException("An OAuth2 resource must be supplied.");
        }

        this.resource = resource;
        this.context = context;
        setErrorHandler(new OAuth2ErrorHandler(resource));
    }

RestTemplate构造函数默认将messageConverters放置。

public RestTemplate() {
        this.messageConverters.add(new ByteArrayHttpMessageConverter());
        this.messageConverters.add(new StringHttpMessageConverter());
......

也许一种解决方案是,一旦创建了对象OAuth2RestTemplate,您就可以通过此方法(在RestTemplate类内部)使用具有您感兴趣的MediaType的restTemplate.setMessageConverters(messageConverters)来删除以前的对象:

public void setMessageConverters(List<HttpMessageConverter<?>> messageConverters) {
        Assert.notEmpty(messageConverters, "At least one HttpMessageConverter required");
        // Take getMessageConverters() List as-is when passed in here
        if (this.messageConverters != messageConverters) {
            this.messageConverters.clear();
            this.messageConverters.addAll(messageConverters);
        }
    }

编辑: Members calling

如果您看到上面的图片,则可以看到执行的时间:

restTemplate.getAccessToken();

致电

getRequestCallback(resource, form, headers), extractor, form.toSingleValueMap());

protected RequestCallback getRequestCallback(OAuth2ProtectedResourceDetails resource,
        MultiValueMap<String, String> form, HttpHeaders headers) {
    return new OAuth2AuthTokenCallback(form, headers);
}

看看他的构造函数做什么:

/**
     * Request callback implementation that writes the given object to the request stream.
     */
    private class OAuth2AuthTokenCallback implements RequestCallback {

        private final MultiValueMap<String, String> form;

        private final HttpHeaders headers;

        private OAuth2AuthTokenCallback(MultiValueMap<String, String> form, HttpHeaders headers) {
            this.form = form;
            this.headers = headers;
        }

        public void doWithRequest(ClientHttpRequest request) throws IOException {
            request.getHeaders().putAll(this.headers);
            request.getHeaders().setAccept(
                    Arrays.asList(MediaType.APPLICATION_JSON, MediaType.APPLICATION_FORM_URLENCODED));
            FORM_MESSAGE_CONVERTER.write(this.form, MediaType.APPLICATION_FORM_URLENCODED, request);
        }
    }

也许通过继承和覆盖方法,您可以将您感兴趣的标头传递给该生成器。

还在OAuth2AccessTokenSupport中,retrieveToken方法具有有趣的注释:

// Prepare headers and form before going into rest template call in case the URI is affected by the result
authenticationHandler.authenticateTokenRequest(resource, form, headers);
// Opportunity to customize form and headers
tokenRequestEnhancer.enhance(request, resource, form, headers);

希望我能对您有所帮助。

答案 1 :(得分:0)

您可以使用拦截器包装请求,请参见ClientHttpRequestInterceptor#intercept

  

拦截

ClientHttpResponse intercept(HttpRequest request,
                             byte[] body,
                             ClientHttpRequestExecution execution)
                      throws IOException 
     

拦截给定的请求,并返回响应。给定的ClientHttpRequestExecution允许拦截器将请求和响应传递给链中的下一个实体。

     

此方法的典型实现将遵循以下模式:

     
      
  1. 检查请求和正文
  2.   
  3. (可选)包装请求以过滤HTTP属性。
  4.   
  5. (可选)修改请求的正文。
  6.   

  7.   使用ClientHttpRequestExecution.execute(org.springframework.http.HttpRequest, byte[])执行请求,
      
      不要执行完全阻止执行的请求。
  8.   
  9. (可选)包装响应以过滤HTTP属性。
  10.   

您修改的代码:

OAuth2RestTemplate restTemplate = new OAuth2RestTemplate(ccDetails, context);
restTemplate.setInterceptors(Arrays.asList(new new RestTemplateHeaderModifierInterceptor()));
OAuth2AccessToken accessToken = restTemplate.getAccessToken();

public class RestTemplateHeaderModifierInterceptor implements ClientHttpRequestInterceptor {

    @Override
    public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
        HttpRequest requestWrapper = new CustomHttpRequestWrapper(request);
        return execution.execute(requestWrapper, body);
    }
}

public class CustomHttpRequestWrapper extends HttpRequestWrapper {

    public CustomHttpRequestWrapper(HttpRequest request) {
        super(request)
    }        

    @Override
    public HttpHeaders getHeaders() {
        // return all headers, but change the charset
    }
}