【问题标题】:Spring Boot OAuth Always redirecting to HTTP (IBM Cloud CF + Spring Boot 2)Spring Boot OAuth 始终重定向到 HTTP (IBM Cloud CF + Spring Boot 2)
【发布时间】:2018-12-26 12:33:43
【问题描述】:

在 IBM Cloud CF Java Buildpack 上使用 Spring Boot OAuth 2...

https://github.com/ericis/oauth-cf-https-issue

*我已经尝试了以下所有组合。

使用此配置,应用程序陷入无限的重定向循环,其中 OAuth 重定向策略将其发送到 http,然后此配置将其发送到 https

http.requiresChannel().anyRequest().requiresSecure()

如果没有此配置,用户可以通过 http 登录(不需要)。

完整配置:

http.
  requiresChannel().anyRequest().requiresSecure().
  authorizeRequests().
    // allow access to...
    antMatchers("favicon.ico", "/login", "/loginFailure", "/oauth2/authorization/ghe")
    .permitAll().anyRequest().authenticated().and().oauth2Login().
    // Codify "spring.security.oauth2.client.registration/.provider"
    clientRegistrationRepository(this.clientRegistrationRepository()).
    // setup OAuth2 client service to use clientRegistrationRepository
    authorizedClientService(this.authorizedClientService()).
    successHandler(this.successHandler()).
    // customize login pages
    loginPage("/login").failureUrl("/loginFailure").
    userInfoEndpoint().
      // customize the principal
      userService(this.userService());

我也试过了:

  1. 使用https的服务器配置

    server:
      useForwardHeaders: true
      tomcat:
        protocolHeader: x-forwarded-proto
    
  2. Servlet 过滤器

    import java.io.IOException;
    
    import javax.servlet.Filter;
    import javax.servlet.FilterChain;
    import javax.servlet.FilterConfig;
    import javax.servlet.ServletException;
    import javax.servlet.ServletRequest;
    import javax.servlet.ServletResponse;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.stereotype.Component;
    
    @Component
    public class HttpToHttpsFilter implements Filter {
    
      private static final Logger log = LoggerFactory.getLogger(HttpToHttpsFilter.class);
    
      private static final String HTTP = "http";
      private static final String SCHEME_HTTP = "http://";
      private static final String SCHEME_HTTPS = "https://";
      private static final String LOCAL_ID = "0:0:0:0:0:0:0:1";
      private static final String LOCALHOST = "localhost";
    
      @Value("${local.ip}")
      private String localIp;
    
      public HttpToHttpsFilter() {
        // Sonar
      }
    
      @Override
      public void doFilter(final ServletRequest req, final ServletResponse res, final FilterChain chain)
          throws IOException, ServletException {
    
        final HttpServletRequest request = (HttpServletRequest) req;
    
        final HttpServletResponse response = (HttpServletResponse) res;
    
        // http, not localhost, not localhost ipv6, not local IP
        if (HTTP.equals(request.getScheme()) && 
            !LOCALHOST.equals(request.getRemoteHost()) && 
            !LOCAL_ID.equals(request.getRemoteHost()) && 
            (this.localIp != null && !this.localIp.equals(request.getRemoteHost()))) {
    
          final String query = request.getQueryString();
    
          String oldLocation = request.getRequestURL().toString();
    
          if (query != null) {
            oldLocation += "?" + query;
          }
    
          final String newLocation = oldLocation.replaceFirst(SCHEME_HTTP, SCHEME_HTTPS);
    
          try {
    
            log.info("HTTP redirect from {} to {} ", oldLocation, newLocation);
    
            response.sendRedirect(newLocation);
    
          } catch (IOException e) {
            log.error("Cannot redirect to {} {} ", newLocation, e);
          }
        } else {
          chain.doFilter(req, res);
        }
      }
    
      @Override
      public void destroy() {
        // Sonar
      }
    
      @Override
      public void init(FilterConfig arg0) throws ServletException {
        // Sonar
      }
    }
    

依赖关系

dependencies {

    //
    // BASICS

    // health and monitoring
    // compile('org.springframework.boot:spring-boot-starter-actuator')

    // security
    compile('org.springframework.boot:spring-boot-starter-security')

    // configuration
    compile('org.springframework.boot:spring-boot-configuration-processor')

  //
  // WEB

  // web
  compile('org.springframework.boot:spring-boot-starter-web')

  // thymeleaf view render
  compile('org.springframework.boot:spring-boot-starter-thymeleaf')

  // thymeleaf security extras
  compile('org.thymeleaf.extras:thymeleaf-extras-springsecurity4')

  //
  // OAUTH

  // OAuth client
  compile('org.springframework.security:spring-security-oauth2-client')

  // OAuth lib
  compile('org.springframework.security:spring-security-oauth2-jose')

  // OAuth config
  compile('org.springframework.security.oauth.boot:spring-security-oauth2-autoconfigure:2.0.0.RELEASE')

  //
  // CLOUD

  // cloud connectors (e.g. vcaps)
  compile('org.springframework.boot:spring-boot-starter-cloud-connectors')

    //
    // TOOLS

    runtime('org.springframework.boot:spring-boot-devtools')

    //
    // TEST

    // test
    testCompile('org.springframework.boot:spring-boot-starter-test')

    // security test
    testCompile('org.springframework.security:spring-security-test')
}

【问题讨论】:

    标签: java spring-boot spring-security ibm-cloud spring-security-oauth2


    【解决方案1】:

    由于使用了 Spring 5,决定采用下一个解决方案(安全配置):

            http
                .addFilterBefore(new ForwardedHeaderFilter(), OAuth2AuthorizationRequestRedirectFilter.class)
    

    x-forwarded-* 标头参数现在处理正确。

    【讨论】:

      【解决方案2】:

      即使不涉及 OAuth 行为,应用程序在当前设置的环境中也可能总是陷入无限循环。

      虽然您可以告诉服务器使用转发标头,

      server:
        useForwardHeaders: true
      

      Tomcat 不会信任来自所有来源的 x-forwarded-* 标头。默认情况下,某些 IP 地址被视为内部地址 (RemoteIpValve#internalProxies)。

      但是,在您使用的环境中,报告的代理 IP 地址可能不在此范围内。您可以使用以下配置允许所有 IP 地址:

      server:
        tomcat:
          internal-proxies: .*
      

      这允许所有代理,但如果无法直接访问应用程序,可能会满足您的需求。

      【讨论】:

      • 我没有尝试 server.tomcat.internal-proxies 设置,只是因为默认值是基于正则表达式的动态范围,并且将值设置为 .* 是不应该用于的安全风险生产。
      • Spring Boot 自动配置 Tomcat 为RemoteIpValve,可以配置为server.tomcat.remote-ip-headerserver.tomcat.protocol-header (docs.spring.io/spring-boot/docs/current/reference/html/…)。 Spring 5 使用 ForwardedHeaderFilter (github.com/spring-projects/spring-framework/wiki/…) 简化了这一点。您的回答对旧版本很有帮助且有效。由于 Spring Boot 2 需要 Spring Framework 5,我将留下ForwardedHeaderFilter 作为官方答案。
      【解决方案3】:

      已解决。有关与此相关的问题的详细信息,请访问: https://github.com/spring-projects/spring-security/issues/5535#issuecomment-407413944

      正在运行的示例项目:https://github.com/ericis/oauth-cf-https-issue

      简短回答:

      需要明确配置应用程序以了解代理标头。我尝试过配置,但最终不得不使用最近添加到 Spring 中的 ForwardedHeaderFilter 类的实例。

      @Bean
      FilterRegistrationBean<ForwardedHeaderFilter> forwardedHeaderFilter() {
      
          final FilterRegistrationBean<ForwardedHeaderFilter> filterRegistrationBean = new FilterRegistrationBean<ForwardedHeaderFilter>();
      
          filterRegistrationBean.setFilter(new ForwardedHeaderFilter());
          filterRegistrationBean.setOrder(Ordered.HIGHEST_PRECEDENCE);
      
          return filterRegistrationBean;
      }
      

      【讨论】:

        猜你喜欢
        • 2019-11-10
        • 1970-01-01
        • 2019-02-18
        • 2021-09-25
        • 2014-12-26
        • 1970-01-01
        • 2021-03-31
        • 2019-05-15
        • 1970-01-01
        相关资源
        最近更新 更多