国产一级a片免费看高清,亚洲熟女中文字幕在线视频,黄三级高清在线播放,免费黄色视频在线看

打開APP
userphoto
未登錄

開通VIP,暢享免費(fèi)電子書等14項(xiàng)超值服

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

情景

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),可以在效果上看起來是登出了,但是并沒有完全登出。

即:
C1和C2的JSESSIONID對(duì)應(yīng)在服務(wù)器的session被銷毀,瀏覽器兩個(gè)JSESSIONID失效(看起來登出了)
cas的cookie(TGT)失效
C1服務(wù)器上,對(duì)應(yīng)的用戶權(quán)限清除(C1是完全退出了)
C2服務(wù)器上,對(duì)應(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銷毀掉改用戶對(duì)應(yīng)的session,而并沒有調(diào)用shiro的subject.logout, 顯然,subject.logout是做了銷毀權(quán)限緩存等操作的

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

解決方案

方案一

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

方案二

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

方案三

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

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

坑一

問題:

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

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

坑二

問題:

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

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

坑三

問題:

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

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

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

所以這里,要把SingleSignOutFilter和Shiro自己提供的CasFilter合并起來,放在一起作為一個(gè)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);
}

代碼邏輯很簡(jiǎn)單,主要是要找到這么個(gè)解決方案,得一點(diǎn)點(diǎn)的調(diào)試和摸索,也是蠻有意思。
另外web.xml中的SingleSignOutFilter需要去掉,因?yàn)槲覀円呀?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)備找個(gè)時(shí)間寫個(gè)cas的faq,畢竟在開發(fā)過程中,遇到的很多常見問題,很是煩躁。

本站僅提供存儲(chǔ)服務(wù),所有內(nèi)容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊舉報(bào)。
打開APP,閱讀全文并永久保存 查看更多類似文章
猜你喜歡
類似文章
SSO(單點(diǎn)登錄)實(shí)現(xiàn)機(jī)制講解
apache shiro org.apache.shiro.session.UnknownSessionException處理
我的shiro之旅: 十三 shiro 用戶的登錄與退出
第十八章 并發(fā)登錄人數(shù)控制
第二十四章 在線會(huì)話管理——《跟我學(xué)Shiro》
容易實(shí)現(xiàn)Shiro單點(diǎn)登錄(自定義Token令牌)
更多類似文章 >>
生活服務(wù)
分享 收藏 導(dǎo)長(zhǎng)圖 關(guān)注 下載文章
綁定賬號(hào)成功
后續(xù)可登錄賬號(hào)暢享VIP特權(quán)!
如果VIP功能使用有故障,
可點(diǎn)擊這里聯(lián)系客服!

聯(lián)系客服