2016-12-06 2 views
0

В webapp, использующем зависимость pac4j для реализации поддержки единого входа, я столкнулся с проблемой.Проблема отмены авторизации аутентификации SSO с pac4j (несколько провайдеров)

Контекст:

  • Java EE/JRE 1.7.0.79, Tomcat 7.0.70, org.springframework: весна: 3.2.16.RELEASE, org.springframework.security:spring-security-core:3.2 .9.RELEASE, org.pac4j: spring-security-pac4j: 1.4.1, org.pac4j: pac4j-oauth: 1.8.3, org.pac4j: pac4j-saml: 1.8.3
  • Множественная сторонняя аутентификация провайдеры включены в конфигурацию webapp (например, Google OAuth и любой SAML), перенаправленные в UI в виде 2 кнопок на странице входа: «Войти в Google», «Войти с помощью my_SAML_provider_label»

Требования:

  • Модернизация Java и/или Tomcat является опцией. Обновление весна и pac4j не
  • Не следует использовать Spring аннотации инъекции, в любое время можно

выпуска конечного пользователя последовательность:

  • 1/Нажмите "Вход с Google" (UserAgent перенаправляется аутентификации от Google страница)
  • 2/Проверьте подлинность на странице Google с помощью внешнего пользователя, который будет или не будет соответствовать ни одному из ваших пользователей локального приложения после обратного вызова
  • 3/Вернуться к локальной странице входа в систему webapp
  • 4/Нажмите «Вход с my_SAML_provider_label» сейчас (UserAgent перенаправляются на страницу аутентификации провайдера)
  • 5/Аутентифицировать правильно на странице третьей стороны, с внешним пользователем, который будет или не соответствует ни одному из ваших локальные пользователи приложения Upon обратного вызова
  • 6/Утверждают следующее исключение в журналах: org.pac4j.oauth.profile.google2.Google2Profile не может быть приведен к org.pac4j.saml.profile.SAML2Profile

Выпуск StackTrace:

java.lang.ClassCastException: org.pac4j.oauth.profile.google2.Google2Profile cannot be cast to org.pac4j.saml.profile.SAML2Profile 
at com.company.module.sso.SAMLAuthenticationService.retrieveAuthenticatedUser(SAMLAuthenticationService.java:59) 
.. 
at org.apache.struts.action.RequestProcessor.processActionPerform(RequestProcessor.java:484) 
at org.apache.struts.action.RequestProcessor.process(RequestProcessor.java:274) 
at org.apache.struts.action.ActionServlet.process(ActionServlet.java:1482) 
at org.apache.struts.action.ActionServlet.doGet(ActionServlet.java:507) 
at javax.servlet.http.HttpServlet.service(HttpServlet.java:624) 
at javax.servlet.http.HttpServlet.service(HttpServlet.java:731) 
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:303) 
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208) 
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52) 
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241) 
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208) 
at com.company.module.filters.ApplicationAvailabilityFilter.doFilter(ApplicationAvailabilityFilter.java:59) 
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241) 
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208) 
at com.company.module.filters.LogFilter.doFilter(LogFilter.java:57) 
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241) 
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208) 
at com.company.module.filters.ChronoFilter.doFilter(ChronoFilter.java:78) 
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241) 
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208) 
at com.company.module.filters.HibernateFilter.doFilter(HibernateFilter.java:53) 
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241) 
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208) 
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330) 
at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:118) 
at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:84) 
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342) 
at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:113) 
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342) 
at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:103) 
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342) 
at org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:113) 
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342) 
at org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:154) 
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342) 
at org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:45) 
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342) 
at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:199) 
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342) 
at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:110) 
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342) 
at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:50) 
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:106) 
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342) 
at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:87) 
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342) 
at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:192) 
at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:160) 
at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:343) 
at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:260) 
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241) 
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208) 
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:218) 
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:122) 
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:505) 
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:169) 
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:103) 
at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:956) 
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:116) 
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:442) 
at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1082) 
at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:623) 
at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:318) 
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145) 
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615) 
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) 
at java.lang.Thread.run(Thread.java:745) 

Связанные исходный код:

ApplicationContext-security.xml:

<?xml version="1.0" encoding="UTF-8"?> 
<beans:beans 
    xmlns="http://www.springframework.org/schema/security" 
    xmlns:beans="http://www.springframework.org/schema/beans" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xsi:schemaLocation=" 
     http://www.springframework.org/schema/beans 
     http://www.springframework.org/schema/beans/spring-beans-3.2.xsd 
     http://www.springframework.org/schema/security 
     http://www.springframework.org/schema/security/spring-security-3.2.xsd"> 
    .. 
    <beans:bean id="clientFilter" class="org.pac4j.springframework.security.web.ClientAuthenticationFilter"> 
     <beans:constructor-arg value="/outer-authentication"/> 
     <beans:property name="clients" ref="clients" /> 
     <beans:property name="sessionAuthenticationStrategy" ref="sas" /> 
     <beans:property name="authenticationManager" ref="authenticationManager" /> 
    </beans:bean> 
    .. 
    <beans:bean id="sas" class="org.springframework.security.web.authentication.session.SessionFixationProtectionStrategy" /> 
</beans:beans> 

SAMLAuthenticationService.Java:

.. 
ClientAuthenticationToken token = null; 
try { 
    token = (ClientAuthenticationToken) SecurityContextHolder.getContext().getAuthentication(); 
    final SAML2Profile samlProfile = (SAML2Profile) token.getUserProfile(); // L59 
    .. 
} finally { 
    token.eraseCredentials(); // troubleshooting: not clearing credentials made no difference 
} 
.. 

Наблюдение:

  • Тот же вопрос можно получить по первой попытке входа через провайдера SAML, а затем через один Google: порядка пользователя последовательности кажется неактуальной
  • A Обходной путь - остановить Tomcat, очистить его рабочий каталог, а затем перезапустить его.
  • В любом случае, нужно дождаться, когда начальный токен аутентификации (вызванный/полученный от провайдера 1) истечет (истечение срока задержки установлено на 1 час через конфигурацию ввода/вывода pac4j)
  • выпуск будет ударяться снова, как только неисправное последовательность выполняется снова конечным пользователем

Guess:

  • , связанные с ненадлежащей отзывом маркера аутентификации предыдущей аутентификации SSO (callbacked/получен от поставщика 1), прежде чем пытаться прочитать токен аутентификации текущего процесса аутентификации (обратный вызов/полученный от провайдера 2)
  • Косвенно из-за неправильного использования org.springframework.security.web.authentication.session. SessionAuthenticationStr ategy (моя реализация в конфигурации XML Spring Security кажется стандартным/по умолчанию)

Благодаря

ответ

0

Solved кажется: должна быть быть вызвана Спринг ценной бумага SecurityContextHolder.clearContext(); до любой ситуации, когда переход от одного провайдера к другому может произойти.

Таких ситуаций могут быть:

  • удаленного пользователь был успешно аутентифицирован поставщиком SSO, но не соответствует нет локального пользователя
  • локального пользователь уже подписан на локальном-приложении через SSO провайдера, но просматривается или перенаправляется на домашнюю страницу локального приложения, а затем может попытаться выполнить аутентификацию SSO через другого поставщика единого входа
  • локальный пользователь спросил или перенаправлен на URL выхода: при уничтожении веб-сессии это также должно быть очистить контекст безопасности Spring

Я еще не тестировал сценарии параллелизма (1 аутентификация локального пользователя через разные браузеры через разные поставщики единого входа, несколько пользователей), поэтому даже если я могу утверждать, что начальная проблема решена, у нее могут быть побочные эффекты (удаление контекст предполагаемого пользователя (желательно), но и другие контексты пользователей (нежелательны)).

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

Смежные вопросы