如何在 spring oauth2 OAuth2AccessToken 请求上设置代理或如何覆盖 OAuth2AccessTokenSupport restTemplate 变量?

Posted

技术标签:

【中文标题】如何在 spring oauth2 OAuth2AccessToken 请求上设置代理或如何覆盖 OAuth2AccessTokenSupport restTemplate 变量?【英文标题】:How to set proxy on spring oauth2 OAuth2AccessToken request or How to override OAuth2AccessTokenSupport restTemplate variable? 【发布时间】:2016-10-17 16:08:06 【问题描述】:

我尝试通过以下方式设置网络代理,但没有一种方法有效

1:设置 jvm 变量,如 -Dhttp.proxyHost= -Dhttp.proxyPort= ....... 2:创建 Bean。

@Bean
public RestTemplate restTemplate() 
    final String proxyHost = "######"; // host
    final int proxyPort = ####;  // port
    SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();
    factory.setProxy(new Proxy(Type.HTTP, new InetSocketAddress(proxyHost, proxyPort)));
    return new RestTemplate(factory);

但此配置被 OAuth2AccessTokenSupport.restTemplate 覆盖。

所以下面的方法总是返回新创建的rest模板对象。

org.springframework.security.oauth2.client.token.OAuth2AccessTokenSupport

protected RestOperations getRestTemplate() 
    if (restTemplate == null) 
        synchronized (this) 
            if (restTemplate == null) 
                RestTemplate restTemplate = new RestTemplate();
                restTemplate.setErrorHandler(getResponseErrorHandler());
                restTemplate.setRequestFactory(requestFactory);
                restTemplate.setInterceptors(interceptors);
                this.restTemplate = restTemplate;
            
        
    
    if (messageConverters == null) 
        setMessageConverters(new RestTemplate().getMessageConverters());
    
    return restTemplate;

请帮助我在 OAuth 客户端应用程序的其余模板上覆盖或设置代理。

【问题讨论】:

【参考方案1】:

另一种方法是将自定义 AccessTokenProvider 设置为您的 OAuth2RestTemplate。在下面的代码示例中,绕过了 SSL 验证:

@Configuration
public class ConfigLocal 

    @Value("$https.proxyHost")
    private String proxyHost;

    @Value("$https.proxyPort")
    private Integer proxyPort;

    @Value("$https.proxyUser")
    private String proxyUser;

    @Value("$https.proxyPassword")
    private String proxyPassword;

    @Bean
    public OAuth2RestTemplate oauth2RestTemplate(ClientCredentialsResourceDetails clientCredentialsResourceDetails)
            throws KeyManagementException, KeyStoreException, NoSuchAlgorithmException 
        OAuth2RestTemplate restTemplate = new OAuth2RestTemplate(clientCredentialsResourceDetails);

        // Instanciate a new http client with proxy configuration, and bypass SSL Certificate verification
        CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
        credentialsProvider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(proxyUser, proxyPassword));

        HttpClientBuilder httpClientBuilder =
                HttpClients.custom()
                        .setProxy(new HttpHost(proxyHost, proxyPort))
                        .setDefaultCredentialsProvider(credentialsProvider)
                        .setSSLHostnameVerifier(new NoopHostnameVerifier())
                            .setSSLContext(new SSLContextBuilder().loadTrustMaterial(null, (x509Certificates, s) -> true)
                                    .build());

        // requestFactory
        HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory(httpClientBuilder.build());
        ClientCredentialsAccessTokenProvider clientCredentialsAccessTokenProvider = new ClientCredentialsAccessTokenProvider();
        clientCredentialsAccessTokenProvider.setRequestFactory(requestFactory);

        // accessTokenProvider
        AccessTokenProvider accessTokenProvider = new AccessTokenProviderChain(Arrays.<AccessTokenProvider> asList(
                new AuthorizationCodeAccessTokenProvider(), new ImplicitAccessTokenProvider(),
                new ResourceOwnerPasswordAccessTokenProvider(), clientCredentialsAccessTokenProvider));

        restTemplate.setAccessTokenProvider(accessTokenProvider);

        return restTemplate;
    

【讨论】:

【参考方案2】:

这似乎是一个更清洁的解决方案:

在带有@Configuration 的类中添加:

@Bean
public CustomUserInfoRestTemplateCustomizer customUserInfoRestTemplateCustomizer() 
    return new CustomUserInfoRestTemplateCustomizer();


/**
 * Customize the UserInfoRestTemplateCustomizer used by OAuthRestTemplate
 * 
 */
private static class CustomUserInfoRestTemplateCustomizer implements UserInfoRestTemplateCustomizer 
    @Value("$http.custom.connect-timeout:500")
    private int connectTimeout;

    @Value("$http.custom.read-timeout:30000")
    private int readTimeout;

    @Value("$http.custom.proxy-host:")
    private String proxyHost;

    @Value("$http.custom.proxy-port:-1")
    private int proxyPort;

    @Override
    public void customize(OAuth2RestTemplate template) 
        template.setRequestFactory(new ClientHttpRequestFactory() 
            @Override
            public ClientHttpRequest createRequest(URI uri, HttpMethod httpMethod) throws IOException 
                SimpleClientHttpRequestFactory clientHttpRequestFactory = new SimpleClientHttpRequestFactory();
                clientHttpRequestFactory.setConnectTimeout(connectTimeout);
                clientHttpRequestFactory.setReadTimeout(readTimeout);
                if (StringUtils.isNoneEmpty(proxyHost)) 
                    Proxy proxy = new Proxy(Type.HTTP, new InetSocketAddress(proxyHost, proxyPort));
                    clientHttpRequestFactory.setProxy(proxy);
                
                return clientHttpRequestFactory.createRequest(uri, httpMethod);
            
        );
    

【讨论】:

【参考方案3】:

这可能不是一个简单的解决方案。但最终设法通过以下代码在 oauth 请求上设置代理。

注册过滤器

@Override
protected void configure(HttpSecurity http) throws Exception 
    // @formatter:off
    http.antMatcher("/**")
    .authorizeRequests().antMatchers("/webjars/**", "/scripts/**", "/styles/**", "/instances/**", "/#/invalid").permitAll()
    .anyRequest().authenticated()
    .and().csrf().csrfTokenRepository(csrfTokenRepository())
    .and().addFilterAfter(csrfHeaderFilter(), CsrfFilter.class)
    .addFilterBefore(oauthFilter(), BasicAuthenticationFilter.class);
    // @formatter:on
    super.configure(http);

身份验证过滤器

@Autowired
OAuth2ClientContext oauth2ClientContext;

@Autowired
OAuth2ProtectedResourceDetails resource;

@Autowired
ResourceServerProperties resourceServer;

@Autowired
RequestHelper requestHelper;

private Filter oauthFilter() throws KeyManagementException, NoSuchAlgorithmException, KeyStoreException 
    OAuth2ClientAuthenticationProcessingFilter oauthFilter = new OAuth2ClientAuthenticationProcessingFilter("/login");
    OAuth2RestTemplate oauthTemplate = new OAuth2RestTemplate(resource, oauth2ClientContext);
    OAuth2AccessTokenSupport authAccessProvider = new AuthorizationCodeAccessTokenProvider();
    // Set request factory for '/oauth/token'
    authAccessProvider.setRequestFactory(requestHelper.getRequestFactory());
    AccessTokenProvider accessTokenProvider = new AccessTokenProviderChain(Arrays.<AccessTokenProvider> asList(
            (AuthorizationCodeAccessTokenProvider)authAccessProvider));
    oauthTemplate.setAccessTokenProvider(accessTokenProvider);
    // Set request factory for '/userinfo'
    oauthTemplate.setRequestFactory(requestHelper.getRequestFactory());
    oauthFilter.setRestTemplate(oauthTemplate);
    UserInfoTokenServices userInfoTokenService = new UserInfoTokenServices(resourceServer.getUserInfoUri(), resource.getClientId());
    userInfoTokenService.setRestTemplate(oauthTemplate);
    oauthFilter.setTokenServices(userInfoTokenService);
    return oauthFilter;

请求帮助代码

@Configuration
public class RequestHelper 

  @Value("$proxy.hostname")
  private String proxyHost;

  @Value("$proxy.port")
  private int proxyPort;

  @Value("$proxy.username")
  private String proxyUser;

  @Value("$proxy.password")
  private String proxyPassword;

  @Value("$useProxy")
  private boolean useProxyFlag;

  @Value("$skipSslValidation")
  private Boolean skipSslValidationFlag;

  public HttpComponentsClientHttpRequestFactory getRequestFactory() throws KeyManagementException, NoSuchAlgorithmException, KeyStoreException 

      HttpClientBuilder httpClientBuilder = HttpClients.custom();

      // Skip SSL validation based on condition
      if (skipSslValidationFlag) 
          TrustStrategy acceptingTrustStrategy = (X509Certificate[] chain, String authType) -> true;

          SSLContext sslContext = org.apache.http.ssl.SSLContexts.custom()
                  .loadTrustMaterial(null, acceptingTrustStrategy)
                  .build();
          SSLConnectionSocketFactory csf = new SSLConnectionSocketFactory(sslContext);

          httpClientBuilder = httpClientBuilder.setSSLSocketFactory(csf);
      

      // Set proxy based on condition
      if (useProxyFlag) 
          CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
          credentialsProvider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(proxyUser, proxyPassword));
          httpClientBuilder = httpClientBuilder.setProxy(new HttpHost(proxyHost, proxyPort));
          httpClientBuilder = httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider);
      

      CloseableHttpClient httpClient = httpClientBuilder.build();
      HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory();
      requestFactory.setHttpClient(httpClient);
      return requestFactory;
  

【讨论】:

【参考方案4】:

万一有人从 Google 登陆...

我们遇到了与描述相同的问题。花了几天时间试图修复它,包括尝试上面的帖子。我们通过设置 JVM 代理 args 无协议来修复它!

不正确:-Dhttp.proxyHost=http://some.host

正确:-Dhttp.proxyHost=some.host

【讨论】:

以上是关于如何在 spring oauth2 OAuth2AccessToken 请求上设置代理或如何覆盖 OAuth2AccessTokenSupport restTemplate 变量?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 spring oauth2 OAuth2AccessToken 请求上设置代理或如何覆盖 OAuth2AccessTokenSupport restTemplate 变量?

如何在我的 Spring Boot Security OAuth2 应用程序中仅为某些类启用 OAuth2?

如何在 Spring Webflux 中自定义 Oauth2 的登录页面?

如何在 Spring Security 中动态指定 OAuth2 资源详细信息?

如何在 Spring Boot 中使用 IPwhitelisting 和 OAuth2? [复制]

Spring Oauth2:如何获取所有clientIds?