簡體   English   中英

pac4j的SSO身份驗證令牌吊銷問題(多個提供程序)

[英]SSO authentication token revocation issue with pac4j (multiple providers)

在使用pac4j依賴項實現SSO支持的webapp中,遇到一個問題。

內容:

  • Java EE / JRE 1.7.0.79,Tomcat 7.0.70,org.springframework:spring: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),並通過登錄頁面上的2個按鈕轉發到UI:“使用Google登錄”,“使用my_SAML_provider_label登錄”

要求:

  • 可以選擇升級Java或Tomcat。 不升級spring和pac4j
  • 盡可能不要使用Spring注解注入

發出最終用戶順序:

  • 1 /單擊“使用Google登錄”(用戶代理被重定向到Google的身份驗證頁面)
  • 2 /在外部頁面上正確或正確地進行身份驗證,外部用戶在回調時將與您的任何本地應用程序用戶匹配
  • 3 /返回本地Webapp登錄頁面
  • 4 /立即單擊“使用my_SAML_provider_label登錄”(useragent重定向到提供程序身份驗證頁面)
  • 5 /在第三方頁面上正確驗證身份,外部用戶在回調時將與您的任何本地應用程序用戶匹配,也可能不匹配
  • 6 /在日志中聲明以下異常:org.pac4j.oauth.profile.google2.Google2Profile無法強制轉換為org.pac4j.s​​aml.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嘗試登錄來獲得相同的問題:用戶順序順序似乎無關緊要
  • 一種解決方法是停止Tomcat,清理其工作目錄,然后重新啟動它
  • 兩種方法都等待初始身份驗證令牌(從提供者1回調/獲取)到期(通過pac4j配置I / O將過期延遲設置為1h)
  • 一旦最終用戶再次執行了錯誤的順序,問題將再次發生

猜測:

  • 在嘗試讀取當前身份驗證過程(從提供者2回調/獲取)之前,與先前SSO身份驗證的不正確吊銷(從提供者1回調/獲取)有關。
  • 間接地,由於對org.springframework.security.web.authentication.session.SessionAuthenticationStrategy的使用不當(我在Spring Security XML配置中的實現似乎是標准/默認設置)

謝謝

似乎解決了:Spring安全性的SecurityContextHolder.clearContext(); 在可能發生從一個提供程序到另一個提供程序的任何跳轉之前,必須已調用該文件。

這種情況可能是:

  • 遠程用戶已由SSO提供者成功認證,但不匹配本地用戶
  • 本地用戶已經通過SSO提供程序在本地應用程序上簽名,但是已瀏覽或重定向到本地應用程序主頁,然后可以嘗試通過另一個SSO提供程序進行SSO身份驗證
  • 本地用戶詢問或重定向到注銷URL:在銷毀Web會話時,這還應清除Spring安全性的上下文

我尚未測試並發方案(1個本地用戶通過不同的瀏覽器通過不同的SSO提供程序,多個用戶進行身份驗證),因此即使我可以斷言初始問題已解決,也可能會有副作用(丟棄目標用戶)上下文(不需要),但其他用戶上下文(不需要))。

用戶身份驗證存在於1個會話中,而安全上下文存在於1個線程中。 所以我有些失落,無法完全掌握它。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM