小男孩‘自慰网亚洲一区二区,亚洲一级在线播放毛片,亚洲中文字幕av每天更新,黄aⅴ永久免费无码,91成人午夜在线精品,色网站免费在线观看,亚洲欧洲wwwww在线观看

分享

cas+shiro單點(diǎn)登出的坑 | Server Not Down

 KILLKISS 2017-04-13

情景

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

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

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

原理分析

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都很正常,問(wèn)題出在第四步。

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

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

解決方案

方案一

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

方案二

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

方案三

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

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

坑一

問(wèn)題:

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

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

坑二

問(wèn)題:

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

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

坑三

問(wèn)題:

SingleSignOutFilter是在ShiroFilter chain之前,也就是說(shuō),如果重寫(xiě)SingleSignOutFilter,在里邊連一個(gè)不包含Principal的Subject都獲取不到,但是如果把這個(gè)SimplePrincipalCollection放到 shrioFilter之后,登錄的時(shí)候又會(huì)有問(wèn)題
這是一個(gè)雞生蛋和蛋生雞的問(wèn)題啊。。。

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

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

所以這里,要把SingleSignOutFilter和Shiro自己提供的CasFilter合并起來(lái),放在一起作為一個(gè)filter

方案三代碼

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

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之后,還要銷(xiāo)毀session SingleSignOutFilter做的事情
handler.destroySession(request);
return;
} else {
log.trace("Ignoring URI " + request.getRequestURI());
}
filterChain.doFilter(servletRequest, servletResponse);
}

代碼邏輯很簡(jiǎn)單,主要是要找到這么個(gè)解決方案,得一點(diǎn)點(diǎn)的調(diào)試和摸索,也是蠻有意思。
另外web.xml中的SingleSignOutFilter需要去掉,因?yàn)槲覀円呀?jīng)移到Shiro里邊了,但是Listener需要保留,并且需要自己重寫(xiě)(里邊有調(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 都走我們剛剛寫(xiě)的filter-->
/logout = logout
/** = user
</value>
</property>
</bean>

最后

準(zhǔn)備找個(gè)時(shí)間寫(xiě)個(gè)cas的faq,畢竟在開(kāi)發(fā)過(guò)程中,遇到的很多常見(jiàn)問(wèn)題,很是煩躁。

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

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類(lèi)似文章 更多