redirect_uri使用http而不是https

时间:2018-04-11 02:46:03

标签: spring facebook spring-boot spring-security oauth-2.0

我正在使用spring stack(Spring Boot 2.0.1.RELEASE)来创建一个通过OAuth2将用户身份验证/注册委托给Facebook的网站。当我点击“使用facebook登录”按钮时,我被重定向到Facebook,但Spring Security OAuth2正在使用http而不是https创建redirect_uri参数。该应用程序使用https,我无法弄清楚这个“http”的来源。

那么,我怎样才能让Spring正确创建redirect_uri参数?

更新

抱歉原帖。已经很晚了,我想在睡觉之前发布问题: - )

好吧,我的应用程序使用Spring Boot 2.0.1.RELEASE,它附带了Spring Security 2.0.1.RELEASE和Spring Security OAuth2 5.0.4.RELEASE。我的应用程序使用Facebook来注册和验证用户。目前,我有一个在AWS(Beanstalk)中运行并使用Amazon的SSL证书的测试环境。

当我第一次写帖子时,我的问题是我的应用程序(实际上是SS)向Facebook发送的redirect_uri参数有一个http前缀,而不是https。这导致Facebook出错,只接受https重定向网址。

阅读文档后,我找到了spring.security.oauth2.client.registration.facebook.redirect-uri-template属性,我将其设置为https://[my domain]/login/oauth2/code/{registrationId}。现在Facebook处理我的身份验证请求并将帖子发回我的应用程序。

但是,使用上一个参数集,现在问题已经改变。现在,当Facebook的回调攻击我在AWS的应用程序时,我得到以下异常(来自日志):

2018-04-11 10:51:23 [http-nio-5000-exec-5] DEBUG o.s.s.o.c.w.OAuth2LoginAuthenticationFilter - Request is to process authentication
2018-04-11 10:51:23 [http-nio-5000-exec-5] DEBUG o.s.s.authentication.ProviderManager - Authentication attempt using org.springframework.security.oauth2.client.authentication.OAuth2LoginAuthenticationProvider
2018-04-11 10:51:23 [http-nio-5000-exec-5] DEBUG o.s.s.authentication.ProviderManager - Authentication attempt using org.springframework.security.oauth2.client.oidc.authentication.OidcAuthorizationCodeAuthenticationProvider
2018-04-11 10:51:23 [http-nio-5000-exec-5] DEBUG o.s.s.o.c.w.OAuth2LoginAuthenticationFilter - Authentication request failed: org.springframework.security.oauth2.core.OAuth2AuthenticationException: [invalid_redirect_uri_parameter] 
org.springframework.security.oauth2.core.OAuth2AuthenticationException: [invalid_redirect_uri_parameter] 
    at org.springframework.security.oauth2.client.authentication.OAuth2LoginAuthenticationProvider.authenticate(OAuth2LoginAuthenticationProvider.java:117)
    at org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:174)
    at org.springframework.security.oauth2.client.web.OAuth2LoginAuthenticationFilter.attemptAuthentication(OAuth2LoginAuthenticationFilter.java:159)
    at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:212)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
    at org.springframework.security.oauth2.client.web.OAuth2AuthorizationRequestRedirectFilter.doFilterInternal(OAuth2AuthorizationRequestRedirectFilter.java:128)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
    at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:116)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
    at org.springframework.security.web.csrf.CsrfFilter.doFilterInternal(CsrfFilter.java:100)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
    at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:66)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
    at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:105)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
    at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:56)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
    at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:215)
    at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:178)
    at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:357)
    at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:270)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
    at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:99)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
    at org.springframework.web.filter.HttpPutFormContentFilter.doFilterInternal(HttpPutFormContentFilter.java:109)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
    at org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:81)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
    at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:200)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:198)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96)
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:496)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:140)
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:81)
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:87)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:342)
    at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:803)
    at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66)
    at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:790)
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1459)
    at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
    at java.lang.Thread.run(Thread.java:748)

看一下我发现问题的来源,问题似乎出现在org.springframework.security.oauth2.client.authentication.OAuth2LoginAuthenticationProvider类的以下测试中:

if (!authorizationResponse.getRedirectUri().equals(authorizationRequest.getRedirectUri())) {
    OAuth2Error oauth2Error = new OAuth2Error(INVALID_REDIRECT_URI_PARAMETER_ERROR_CODE);
    throw new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString());
}

要检查此比较失败的原因,我使用Chrome的开发者工具检查了请求和响应。所以,这是对Facebook的呼吁:

https://www.facebook.com/v2.8/dialog/oauth?response_type=code&client_id=[REMOVED]&scope=public_profile%20email&state=[REMOVED]&redirect_uri=https://[REMOVED]/login/oauth2/code/facebook

一切似乎都没问题,redirect_uri参数按预期使用https,完整的redirect_uri似乎正确。

这是Facebook的回调:

https://[REMOVED]/login/oauth2/code/facebook?code=[REMOVED]

再一次,一切似乎都好。但是,SS拒绝用户身份验证,因为请求和响应redirect_uris不匹配。

这就是问题所在。知道这里出了什么问题吗?我错过了什么吗?

6 个答案:

答案 0 :(得分:4)

我遇到的问题与谷歌完全相同。

具有以下微服务架构

Google Auth Server


  Zuul Gateway (:8080)
     /   \
    /     \
   /       \
Other      OAuth2Client (:5000)

在本地计算机上运行时一切正常,但在AWS Elastic Beanstalk中我捕获了同样的异常。

调试之后,我发现在我的情况下,当OAuth2Client落后于Zuul代理(它们在单独的微服务中实现)时,我在OAuth2LoginAuthenticationProvider内的检查中得到了不同的redirect_uri值:

if (!authorizationResponse.getRedirectUri().equals(authorizationRequest.getRedirectUri())) {
    OAuth2Error oauth2Error = new OAuth2Error(INVALID_REDIRECT_URI_PARAMETER_ERROR_CODE);
    throw new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString());
}

所以在我的AWS案例中,我有以下价值观:

authorizationResponse.getRedirectUri()
http://[INNER_AWS_ESB_IP]:5000/auth/login/oauth2/code/google

authorizationRequest.getRedirectUri()
https://[MY_PROJECT_DOMAIN_NAME]/auth/login/oauth2/code/google

其中[INNER_AWS_ESB_IP]是AWS Elastic Beanstalk中内部网络的IP地址,[MY_PROJECT_DOMAIN_NAME]是我的项目的域名,在application.yml中作为redirect-uri-template参数进行了硬编码

我的 OAuth2Client 微服务的application.yml中有以下配置

server:
  port: 5000
  servlet:
     contextPath: /auth
  use-forward-headers: true

spring:
  security:
    oauth2:
      resource:
        filter-order: 3
      client:
        registration:
          google:
            client-id:  [REMOVED]
            client-secret: [REMOVED]
            redirect-uri-template: ${MY_PROJECT_DOMAIN_NAME:http://localhost:8080}/auth/login/oauth2/code/google
            scope: profile,email

洛雷诺,你有什么样的建筑?你可以分享你的配置吗?

更新

似乎该问题与版本科学5.0中的Spring Security Oauth2 Client的实现直接相关

问题可以重现,如果在一些单独的虚拟机和其他微服务上启动Zuul Gateway微服务应该在本地机器上启动☝️所以Google应该从VM上的浏览器调用。

帮助我避免此问题的解决方案是添加自定义Filter自定义HttpServletRequestWrapper,它可以覆盖方法并返回“正确”的URL以满足签入OAuth2LoginAuthenticationProvider.java:115

  1. 在Oauth2客户端的application.yml

    myCloudPath: ${MY_PROJECT_DOMAIN_NAME:http://localhost:8080}

  2. SecurityConfig

    @Value("${myCloudPath}")
    private String myCloudPath;
    
    @Override
    public void configure(HttpSecurity http) throws Exception {
        http.
             addFilterBefore(new MyCustomFilter(myCloudPath), OAuth2LoginAuthenticationFilter.class).
             ...
    
  3. 过滤

    public class MyCustomFilter implements Filter {
    
        private static final Logger logger = LogManager.getLogger(MyCustomFilter.class);
        private String myCloudPath;
    
    
        public MyCustomFilter(String myCloudPath) {
            this.myCloudPath= myCloudPath;
        }
    
        @Override
        public void init(FilterConfig filterConfiguration) throws ServletException {
            logger.info("MyCustomFilter init");
        }
    
        public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
                throws IOException, ServletException {
    
            request = new MyHttpServletRequestWrapper((HttpServletRequest) request, myCloudPath);
    
            chain.doFilter(request, response);
        }
    
        @Override
        public void destroy() {
            logger.info("MyCustomFilter destroy");
        }
    }
    
  4. 了HttpServletRequestWrapper

    public class MyHttpServletRequestWrapper extends HttpServletRequestWrapper {
    
        public final String redirectUrl;
    
        public MyHttpServletRequestWrapper(HttpServletRequest request, String myCloudPath) {
            super(request);
            this.redirectUrl = myCloudPath + request.getRequestURI();
        }
    
        @Override
        public StringBuffer getRequestURL() {
            return new StringBuffer(redirectUrl);
        }
    }
    

答案 1 :(得分:3)

我在设置Spring Boot应用程序以使用Facebook OAuth2实现对用户进行身份验证时遇到了同样的错误。 Nginx(作为反向代理)用于配置Web应用程序,还可以卸载SSL证书。

最初,我尝试自定义属性:redirect-uri-template,以便可以使用https://{domain}/login/oauth2/code/facebook对重定向uri进行硬编码(这是因为Facebook仅接受有效OAuth重定向URI的HTTPS协议)。它没有工作,因为我遇到了同样的错误:OAuth2AuthenticationException: [invalid_redirect_uri_parameter]

然后,我在link找到了建议的解决方案,这对我有用。因此,基本上将OAuth2登录应用程序设置为server.use-forward-headers=true并删除自定义属性:redirect-uri-template

希望有所帮助:)

答案 2 :(得分:1)

在OpenShift中运行并针对Microsoft Azure进行身份验证时,我们面临相同的问题。过滤似乎像是在黑客入侵,*.redirect-uri-template属性现在已过时,并且从Azure返回后,传出和传入的重定向URI不匹配。

经过大量搜索之后,application.properties中的这个简单条目解决了该问题:

server.forward-headers-strategy=framework

答案 3 :(得分:0)

对我来说,这是可行的。 我已经设置

redirect-uri-template: "{baseUrl}/login/oauth2/code/{registrationId}"

并编写了自定义过滤器,将http更改为https

@Slf4j
public class LinkedInRewriteFilter extends OncePerRequestFilter {

private static final String GET_PROTOCOL = "://.*";
private static final String LINKED_IN = "linkedin";
private static final String HTTPS = "https";

@Value("${base-url}")
private String baseUrl;

@Value("${server.port}")
private int serverPort;

@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
    if (request.getRequestURL().toString().contains(LINKED_IN)) {
        request = new LinkedInHttpServletRequestWrapper(request);
    }
    filterChain.doFilter(request, response);
}

public class LinkedInHttpServletRequestWrapper extends HttpServletRequestWrapper {

    public LinkedInHttpServletRequestWrapper(HttpServletRequest request) {
        super(request);
    }

    @Override
    public String getScheme() {
        return baseUrl.replaceFirst(GET_PROTOCOL, StringUtils.EMPTY);
    }

    @Override
    public int getServerPort() {
        return HTTPS.equals(getScheme()) ? 443 : serverPort;
    }
}

}

答案 4 :(得分:0)

对我来说,我必须这样做:

第 1 步:更改您的配置文件

如果使用 application.properties

server.port=8081
server.use-forward-headers: true

如果使用 yml

server:
port: 8081
use-forward-headers : true

第 2 步:更新代理

如果使用 httpd

RequestHeader set X-Forwarded-Proto https
RequestHeader set X-Forwarded-Port 443

ProxyPreserveHost On
ProxyPass / http://localhost:8081/
ProxyPassReverse / http://localhost:8081/

如果使用nginx

location / {
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; # Will add the user's ip to the request, some apps need this
        proxy_set_header X-Forwarded-Proto $scheme; # will forward the protocole i.e. http, https
        proxy_set_header X-Forwarded-Port $server_port; # Will forward the port 
        proxy_set_header Host $host;                    # !Important will forward the host address
        proxy_pass http://localhost:8081/;
}

第 3 步:重启服务器并测试

答案 5 :(得分:0)

我认为新版本的 Spring Security 只是增加了对 .redirect-uri 替换 .redirect-uri-template 的新支持。

依赖项

我正在使用 org.springframework.boot:spring-boot-starter-web:2.3.2.RELEASEorg.springframework.boot:spring-boot-starter-oauth2-client:2.3.2.RELEASEorg.springframework.boot:spring-boot-starter-security:2.3.2.RELEASE

配置文件,例如.properties

在配置文件中,我可以指定:

spring.security.oauth2.client.registration.google.redirect-uri=https://example.com/api/login/oauth2/code/google
<块引用>

请注意,我没有使用 .redirect-uri-template

OAuth2 流程

然后,Spring OAuth2 将请求引导至 Google 服务器,其 URI 如下:

https://accounts.google.com/o/oauth2/v2/auth/oauthchooseaccount?response_type=...&
redirect_uri=https%3A%2F%2Fexample.com%2Fapi%2Flogin%2Foauth2%2Fcode%2Fgoogle
<块引用>

请注意,redirect_uri=https%3A%2F%2Fexample.com 已被编码,其中使用了 https:// 协议。