Redirect_uri с использованием http вместо https

Я использую стек Spring (Spring Boot 2.0.1.RELEASE) для создания сайта, который делегирует аутентификацию / регистрацию пользователя в Facebook через OAuth2. Когда я нажимаю кнопку «Войти через facebook», я перенаправляюсь на Facebook, но Spring Security OAuth2 создает параметр redirect_uri, используя http вместо https. Приложение использует 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) и использующая SSL-сертификат Amazon.

Когда я впервые написал сообщение, моя проблема заключалась в том, что параметр redirect_uri, отправленный моим приложением (на самом деле SS) в Facebook, имел префикс http вместо https. Это вызывало ошибку в Facebook, который принимает только URL-адреса перенаправления 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 не совпадают.

И вот в чем проблема. Есть идеи, что здесь происходит не так? Я что-то пропустил?

было бы неплохо, если бы вы могли поделиться своим файлом конфигурации и свойств. По этой информации сложно что-то угадать.

Agam 11.04.2018 05:50

Я разместил здесь ответ stackoverflow.com/a/61929017/4950185 в похожем вопросе.

Gayan Kavirathne 21.05.2020 09:06

Отвечает ли это на ваш вопрос? Spring OAuth redirect_uri не использует https

Gayan Kavirathne 21.05.2020 09:07
8
3
11 461
6
Перейти к ответу Данный вопрос помечен как решенный

Ответы 6

Ответ принят как подходящий

Я столкнулся с точно такой же проблемой, но с Google.

Имея следующую архитектуру микросервисов

Google Auth Server


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

при работе на локальной машине все работает нормально, но в AWS Elastic Beanstalk я ловлю то же исключение.

После отладки я обнаружил, что в моем случае, когда OAuth2Client находится за прокси Zuul (они реализованы в отдельных микросервисах), я действительно получаю разные значения redirect_uri в проверке внутри OAuth2LoginAuthenticationProvider:

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] - это IP-адрес внутренней сети в AWS Elastic Beanstalk, а [MY_PROJECT_DOMAIN_NAME] - это доменное имя моего проекта, которое жестко запрограммировано в application.yml как параметр redirect-uri-template.

У меня в application.yml микросервиса OAuth2Client есть следующая конфигурация

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

Лорено, какая у тебя архитектура? Можете поделиться своим конфигом?

Обновлено

Похоже, проблема напрямую связана с реализацией клиента Spring Security Oauth2 в версии science 5.0.

Проблема может быть воспроизведена, если запустить микросервис Zuul Gateway на какой-то отдельной виртуальной машине, а другие микросервисы должны быть запущены на локальной машине ☝️ Таким образом, Google должен быть вызван из браузера на виртуальной машине.

Решение, который помогает мне избежать этой проблемы, заключается в добавлении пользовательского Filter с пользовательским HttpServletRequestWrapper, который может переопределять метод и возвращать «правильный» URL-адрес для удовлетворения проверки в OAuth2LoginAuthenticationProvider.java:115 ?

  1. В application.yml клиента Oauth2

    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);
        }
    }
    

Привет, Эндрю, извиняюсь за поздний ответ. Проблема заключалась в разгрузке SSL в среде AWS. Обратный вызов Facebook выполняется с использованием https, но из-за разгрузки SSL Elastic Load Balancer запрос, который действительно попадает в мое приложение, - это http, а не https. Включение use-forward-headers: true в мою конфигурацию решило проблему.

Loreno Oliveira 13.06.2018 12:28

@Andrew, как вы отлаживали uri перенаправления в authorizationRequest и authorizationResponse?

Anatolii Stepaniuk 27.06.2019 21:09

Я столкнулся с той же ошибкой, когда настраивал приложение Spring Boot для аутентификации пользователей с помощью реализации Facebook OAuth2. Nginx (функционирует как обратный прокси) настроен для работы с веб-приложением, а также для разгрузки сертификата SSL.

Первоначально я попытался настроить свойство: redirect-uri-template, чтобы URI перенаправления можно было жестко запрограммировать с помощью https://{domain}/login/oauth2/code/facebook (это потому, что Facebook принимает протокол HTTPS только для действительного URI перенаправления OAuth). Это не сработало, так как я столкнулся с той же ошибкой: OAuth2AuthenticationException: [invalid_redirect_uri_parameter]

Затем я нашел предложенное решение в ссылка на сайт, которое мне подходит. Итак, в основном нужно установить приложение входа OAuth2 с server.use-forward-headers=true и удалить настроенное свойство: redirect-uri-template.

Надеюсь, поможет :)

Подводя итог, здесь в официальном документы

Xtreme Biker 27.06.2019 21:59

@mengjiann, как вы отлаживали, какой именно redirectUri вы получили?

Anatolii Stepaniuk 28.06.2019 08:38

В моем случае мне также пришлось добавить это в мою конфигурацию nginx: proxy_set_header Host $host;proxy_set_header X-Real-IP $remote_addr;proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;proxy_set_header X-Forwarded-Proto $scheme;

Anatolii Stepaniuk 28.06.2019 11:54

Для меня это работает. Я установил

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;
    }
}

}

Привет, Якуб! Как вы отладили, что вы получаете redirectUri с http вместо https?

Anatolii Stepaniuk 28.06.2019 08:41

На самом деле да. Наши приложения используют HTTP, но мы работаем за Apache (443). Итак, я установил apache локально, а затем отладил и выяснил, что UriComponentsBuilder устанавливает HTTP или https в зависимости от того, что использует ваше приложение. Но я обнаружил, что вы можете установить такие свойства, как X-Forwarded-SSL или аналогичные, чтобы изменить это поведение, но я не пробовал. Вы можете проверить UriComponentsBuilder. В моем случае клиент OAuth использует `UriComponentsBuilder.fromHttpUrl (UrlUtils.buildFullRequestU‌ rl (request))

Jakub Krhovják 29.06.2019 20:55

Но если вы хотите использовать мое решение, вы должны установить LinkedIn фильтр, например .addFilterBefore(linkedInRewriteFilter(), OAuth2AuthorizationRequestRedirectFilter.class). Возможно, это можно решить, установив правильные свойства для изменения поведения UriComponentsBuilder, но теперь вы для меня это работает и у вас нет времени ...

Jakub Krhovják 29.06.2019 20:55

Мы столкнулись с той же проблемой при работе в OpenShift и аутентификации в Microsoft Azure. Фильтрация выглядела как взлом, свойства *.redirect-uri-template теперь устарели, и после возврата из Azure исходящие и входящие URI перенаправления не совпадали.

После долгих поисков эта простая запись в application.properties решила проблему:

server.forward-headers-strategy=framework

Для меня мне пришлось сделать следующее:

Шаг 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: Перезапустить сервер и протестировать

Я думаю, что новая версия Spring Security просто добавила новую поддержку .redirect-uri вместо .redirect-uri-template.

Зависимости

я использую org.springframework.boot:spring-boot-starter-web:2.3.2.RELEASE, org.springframework.boot:spring-boot-starter-oauth2-client:2.3.2.RELEASE, org.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

Note that I did not use .redirect-uri-template

OAuth2 Flow

Затем 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

Note that redirect_uri=https%3A%2F%2Fexample.com has been encoded, where https:// protocol is used.

Другие вопросы по теме