乡下人产国偷v产偷v自拍,国产午夜片在线观看,婷婷成人亚洲综合国产麻豆,久久综合给合久久狠狠狠9

  • <output id="e9wm2"></output>
    <s id="e9wm2"><nobr id="e9wm2"><ins id="e9wm2"></ins></nobr></s>

    • 分享

      cas+shiro單點登出的坑 | Server Not Down

       KILLKISS 2017-04-13

      情景

      cas server:cas
      client server: C1
      client server: C2

      當(dāng)用戶在C1和C2都登錄之后,獲取到改用戶在兩個系統(tǒng)內(nèi)各自需要的權(quán)限之后,在C1做登出操作,按照網(wǎng)上大部分的配置方法(web.xml中增加SingleSignOutFilter和SingleSignOutHttpSessionListener),可以在效果上看起來是登出了,但是并沒有完全登出。

      即:
      C1和C2的JSESSIONID對應(yīng)在服務(wù)器的session被銷毀,瀏覽器兩個JSESSIONID失效(看起來登出了)
      cas的cookie(TGT)失效
      C1服務(wù)器上,對應(yīng)的用戶權(quán)限清除(C1是完全退出了)
      C2服務(wù)器上,對應(yīng)的用戶權(quán)限沒有清除(沒完全退出)

      原理分析

      Created with Rapha?l 2.1.2BrowserBrowserC1C1cascasC2C2logout request(1)C1 subject.logout(), redirect to cas (2)cas logout path(3)notify C2 the user had logout(4)

      1,2,3都很正常,問題出在第四步。

      第四步僅僅是被SingleSignOutFilter攔截,根據(jù)service-ticket銷毀掉改用戶對應(yīng)的session,而并沒有調(diào)用shiro的subject.logout, 顯然,subject.logout是做了銷毀權(quán)限緩存等操作的

      這樣就會導(dǎo)致最終C2上的用戶權(quán)限沒有被清除,若在此時用戶權(quán)限被修改,就會導(dǎo)致即使登出,C2上的權(quán)限也沒有刷新

      解決方案

      方案一

      權(quán)限緩存是可以設(shè)置過期時間的,那么簡單點,只要給權(quán)限緩存加上過期時間即可,這樣如果權(quán)限被修改,即使用戶不登出,在過期之后,權(quán)限也會被刷新

      方案二

      http://howiefh./2015/05/19/shiro-cas-single-sign-on/ 有一個很詳細(xì)的說明,但是沒仔細(xì)看,簡單的說就是使用ServletContainerSessionManager,即shiro自己的session管理,似乎可以解決問題,但是未驗證

      方案三

      思路很簡單,重寫SingleSignOutFilter, 在登出的時候,調(diào)用subject.logout 即可。

      奈何太年輕,這種方案有很多坑

      坑一

      問題:

      Subject是由session中存放的一個key生成的,但是時序圖中第四步是有cas發(fā)起的請求,而不是用戶瀏覽器,即這個session中沒有Subject信息,shiro無法獲取到具體信息。

      解決:
      SingleSignOutFilter中有存儲一份 service-ticket與session的映射關(guān)系,那么只要在第四步中 利用 service-ticket取到session,再從session中取到SimplePrincipalCollection信息放入subject即可

      坑二

      問題:

      subject不提供設(shè)置principal接口,service-ticket session映射關(guān)系未提供get接口

      解決:
      反射搞定,但是總覺得不靠譜呢。。

      坑三

      問題:

      SingleSignOutFilter是在ShiroFilter chain之前,也就是說,如果重寫SingleSignOutFilter,在里邊連一個不包含Principal的Subject都獲取不到,但是如果把這個SimplePrincipalCollection放到 shrioFilter之后,登錄的時候又會有問題
      這是一個雞生蛋和蛋生雞的問題啊。。。

      解決:
      問題總是能解決的,放在前邊后邊都不行,那么放一起吧。對,把SingleSignOutFilter放到ShiroFilter之中, 原以為ShiroFilter會對符合過濾規(guī)則的做一個filter chain,結(jié)果并不是。

      shiro會針對配置的filter規(guī)則,取第一個匹配的作為最終的filter,而后邊符合規(guī)則的就會被忽略掉

      所以這里,要把SingleSignOutFilter和Shiro自己提供的CasFilter合并起來,放在一起作為一個filter

      方案三代碼

      經(jīng)過這么一折騰,于是就有了下面的代碼了

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      50
      51
      public void doFilterInternal(final ServletRequest servletRequest, final ServletResponse servletResponse, final FilterChain filterChain) throws IOException, ServletException {
      final HttpServletRequest request = (HttpServletRequest) servletRequest;
      if (handler.isTokenRequest(request)) {
      handler.recordSession(request); // 登錄,記錄session SingleSignOutFilter做的事情
      super.doFilterInternal(servletRequest, servletResponse, filterChain); // 記錄完了之后,就調(diào)用CasFilter自己的doFilterInternal
      return;
      } else if (handler.isLogoutRequest(request)) { // 如果是登出
      // 一堆的代碼,就是為了獲取SimplePrincipalCollection,設(shè)置到Subject里邊去,并在最后調(diào)用subject.logout()
      final String logoutMessage = CommonUtils.safeGetParameter(request, "logoutRequest");
      final String token = XmlUtils.getTextForElement(logoutMessage, "SessionIndex");
      if (CommonUtils.isNotBlank(token)) {
      HttpSession session = null;
      try {
      Field msField = handler.getSessionMappingStorage().getClass().getDeclaredField("MANAGED_SESSIONS");
      msField.setAccessible(true);
      Map<String,HttpSession> MANAGED_SESSIONS = (Map)msField.get(handler.getSessionMappingStorage());
      session = MANAGED_SESSIONS.get(token);
      } catch (Exception e) {
      }
      if (session != null) {
      Subject subject = getSubject(servletRequest, servletResponse);
      ShiroUser shiroUser = (ShiroUser)(((SimplePrincipalCollection)(session.getAttribute("org.apache.shiro.subject.support.DefaultSubjectContext_PRINCIPALS_SESSION_KEY"))).getPrimaryPrincipal());
      SimplePrincipalCollection pc = new SimplePrincipalCollection(shiroUser, shiroUser.getName());
      try {
      Field principalsField = subject.getClass().getSuperclass().getDeclaredField("principals");
      principalsField.setAccessible(true);
      principalsField.set(subject, pc);
      } catch (Exception e) {
      }
      try {
      subject.logout();
      } catch (SessionException ise) {
      }
      }
      }
      // logout之后,還要銷毀session SingleSignOutFilter做的事情
      handler.destroySession(request);
      return;
      } else {
      log.trace("Ignoring URI " + request.getRequestURI());
      }
      filterChain.doFilter(servletRequest, servletResponse);
      }

      代碼邏輯很簡單,主要是要找到這么個解決方案,得一點點的調(diào)試和摸索,也是蠻有意思。
      另外web.xml中的SingleSignOutFilter需要去掉,因為我們已經(jīng)移到Shiro里邊了,但是Listener需要保留,并且需要自己重寫(里邊有調(diào)用SingleSignOutFilter的方法,需要改掉), 代碼如下

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      <bean id="casFilter" class="com.simpletour.sso.shiro.STSingleSignOutFilter">
      <property name="failureUrl" value="${sso.cas.client}${sso.cas.client.home}"/>
      </bean>
      <bean id="shiroFilter" class="com.simpletour.sso.shiro.STShiroFilterFactoryBean" init-method="init">
      <property name="securityManager" ref="securityManager" />
      <property name="loginUrl" value="${sso.cas.server}?service=${sso.cas.client}/cas/login" />
      <property name="successUrl" value="${sso.cas.client.home}" />
      <property name="filters">
      <map>
      <entry key="cas" value-ref="casFilter"/>
      <entry key="logout" value-ref="logoutFilter"/>
      </map>
      </property>
      <property name="filterChainDefinitions">
      <value>
      /static/** = anon
      /config_* = anon
      /cas/* = cas <!--這里,cas/login, cas/logout 都走我們剛剛寫的filter-->
      /logout = logout
      /** = user
      </value>
      </property>
      </bean>

      最后

      準(zhǔn)備找個時間寫個cas的faq,畢竟在開發(fā)過程中,遇到的很多常見問題,很是煩躁。

        本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊一鍵舉報。
        轉(zhuǎn)藏 分享 獻(xiàn)花(0

        0條評論

        發(fā)表

        請遵守用戶 評論公約

        類似文章 更多