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

打開APP
userphoto
未登錄

開通VIP,暢享免費電子書等14項超值服

開通VIP
CAS Client集群環(huán)境的Session問題及解決方案

【原創(chuàng)申明:文章為原創(chuàng),歡迎非盈利性轉(zhuǎn)載,但轉(zhuǎn)載必須注明來源】

之前寫過一篇文章,介紹單點登錄的基本原理。這篇文章重點介紹開源單點登錄系統(tǒng)CAS的登錄和注銷的實現(xiàn)方法。并結(jié)合實際工作中碰到的問題,探討在集群環(huán)境中應(yīng)用單點登錄可能會面臨的問題。這篇文章在上一篇的基礎(chǔ)上,增加了第四部分,最終的解決方案。

1 單點登錄的過程

為了描述方便,假設(shè)有如下一個單點登錄系統(tǒng)。一套CASServer,兩套CAS Client系統(tǒng)。為了描述的方便,省略CAS Server調(diào)用用戶系統(tǒng)完成登錄,以及CASClient從用戶系統(tǒng)讀取用戶詳細信息的過程。

1.1 多應(yīng)用情況下Session信息

假定有兩個CAS Client應(yīng)用,一個CAS Server。應(yīng)用的部署,可能在不同的服務(wù)器,也可能有不同的訪問IP或域名,即使是同一個瀏覽器,在各個應(yīng)用中的Session信息也是不相同的。

瀏覽器中,每個應(yīng)用有一個獨立的JSESSIONIDCookie。某一個應(yīng)用,不可能讀取到瀏覽器在其他應(yīng)用中的Cookie信息。

假定用戶首先訪問CAS Client 01,系統(tǒng)提醒用戶進行一次登錄;然后用戶訪問CAS Client2,不會再提示登錄而是直接登錄成功。

1.2 第一次訪問CAS Client 01

用戶打開瀏覽器后第一次訪問,重定向到單點登錄后,會提示用戶輸入賬號密碼登錄。登錄成功之后,再跳轉(zhuǎn)回CAS Client。

1.3 第一次訪問CAS Client 02

當(dāng)用戶瀏覽器已經(jīng)登錄系統(tǒng),切換到另一個CASClient時,跟第一次訪問有所不同,因為已經(jīng)登錄成功,就不會再提醒輸入賬號密碼登錄了。

1.4 再次訪問CAS Clients

當(dāng)用戶已經(jīng)訪問過CAS Client后,當(dāng)用戶再次訪問,系統(tǒng)不會再跳轉(zhuǎn)到CAS Server做認證。

1.5 CASClient配置

為了實現(xiàn)前述的單點登錄過程,以Java WEB項目為例,需要在 web.xml 中進行相應(yīng)的配置。(為了排版,沒有填寫Filter的完整class名,請自行查閱補充。)

<filter>

  <filter-name>CAS AuthenticationFilter</filter-name>

  <filter-class>*.AuthenticationFilter</filter-class>

</filter>

<filter>

  <filter-name>CAS Validation Filter</filter-name>

  <filter-class>*.Cas10TicketValidationFilter</filter-class>

</filter>

<filter>

  <filter-name>CAS HttpServletRequest WrapperFilter</filter-name>

  <filter-class>*.HttpServletRequestWrapperFilter</filter-class>

</filter>

<filter-mapping>

  <filter-name>CAS Validation Filter</filter-name>

  <url-pattern>/*</url-pattern>

</filter-mapping>

<filter-mapping>

  <filter-name>CAS AuthenticationFilter</filter-name>

  <url-pattern>/*</url-pattern>

</filter-mapping>

<filter-mapping>

         <filter-name>CAS HttpServletRequest WrapperFilter</filter-name>

         <url-pattern>/*</url-pattern>

</filter-mapping>

仔細看一下配置過濾器可以發(fā)現(xiàn),三個過濾器正好對應(yīng)流程圖中三次訪問CAS Client。

  • Authentication Filter:負責(zé)將未登錄用戶跳轉(zhuǎn)到登錄界面

  • Authentication Filter:負責(zé)驗證Service Ticket

  • HttpServletRequest WrapperFilter:負責(zé)將用戶信息封裝到request和session中。

 

 

2 統(tǒng)一注銷的過程

2.1 不能實現(xiàn)統(tǒng)一注銷會有什么問題

當(dāng)用戶訪問系統(tǒng)后從系統(tǒng)注銷,如何能夠從每個應(yīng)用中都注銷?注意前面1.4部分的描述,如果用戶注銷時,并沒有注銷CASClient 02中的會話信息,如果用戶在瀏覽器中直接訪問這個應(yīng)用,因為Session存在,并不會提醒用戶重新登錄。

這會帶來兩個潛在的隱患:

1、  用戶注銷user1后換賬號user2重新登錄,進入CAS Client 02之后,當(dāng)前身份其實還是user1,并沒有如用戶預(yù)期一樣使用user2身份。

2、  用戶user1點擊注銷后離開,沒有關(guān)閉瀏覽器。這時候其他用戶直接打開CAS Client 02,能夠直接盜用user1的身份進行操作。

2.2基本概念:Service、TGT和ST

CAS已經(jīng)考慮到統(tǒng)一注銷的問題。

這里有三個重要的概念TGT、ST和Service,需要著重介紹一下,因為它們同后續(xù)統(tǒng)一注銷的方案息息相關(guān)。

2.2.1  Service

這是用戶第一次訪問CAS Client的URL。假設(shè)一個CAS Client應(yīng)用部署在域名oa.company.com,使用HTTP協(xié)議,應(yīng)用首頁是index.htm。當(dāng)用戶第一次訪問這個應(yīng)用時,對應(yīng)的URL地址是 http://oa.company.com/index.htm 。這個URL,對CAS Server來說,就是一個service。

當(dāng)用戶第一次跳轉(zhuǎn)到CAS Server的時候,可以看到傳了一個參數(shù)service,就是這個值。當(dāng)CASServer生成Ticket重定向到CAS Client的時候,實際就是在這個service 中添加了一個參數(shù) ticket 。

2.2.2   TGT:Ticket Grangting Ticket

TGT是CAS Server為每一個登錄用戶創(chuàng)建的登錄令牌。在CASServer上擁有了TGT,用戶就可以證明自己在CASServer成功登錄過。TGT封裝了SessionCookie值以及此Cookie值對應(yīng)的用戶信息。當(dāng)HTTP請求到來時,CAS以此Cookie值為key查詢緩存中有無TGT ,如果有的話,則相信用戶已登錄過。

2.2.3  ST:Service Ticket

ST是CAS Server為用戶簽發(fā)的訪問某一service的認證令牌。用戶訪問service時,service發(fā)現(xiàn)用戶沒有ST,瀏覽器會跳轉(zhuǎn)到CASServer去獲取ST。CAS Server發(fā)現(xiàn)用戶有TGT,則簽發(fā)一個ST,返回給用戶。用戶使用ST作為ticket參數(shù)去訪問service,service拿ST去CAS Server驗證,驗證通過后,得到當(dāng)前登錄用戶的登錄名。

注意TGT和ST,是一對多的關(guān)系。一個TGT會維護一個 services 列表,每當(dāng)為用戶創(chuàng)建一個ST并認證通過后,會將這個ST添加到TGT的services列表中。這樣,在CASServer端,這個services列表實際維護了一個用戶登錄過的所有CASClient。這就為實現(xiàn)統(tǒng)一注銷打下了基礎(chǔ)。

2.3 CAS Client的統(tǒng)一注銷配置

CAS Client,為了實現(xiàn)統(tǒng)一注銷,除了第一張介紹的三個登錄過程的過濾器之外,還需要添加一個統(tǒng)一注銷過濾器。

<filter>

 <filter-name>CAS Single Sign OutFilter</filter-name>

 <filter-class>*.SingleSignOutFilter</filter-class>

</filter>

<filter-mapping>

 <filter-name>CAS Single Sign OutFilter</filter-name>

 <url-pattern>/*</url-pattern>

</filter-mapping>

<listener>

 <listener-class>*.SingleSignOutHttpSessionListener</listener-class>

</listener>

2.4 CAS Server注銷過程

用戶在瀏覽器中點擊“注銷”鏈接,實際瀏覽器會訪問CASServer的注銷頁面。收到注銷請求后,CAS Server會讀取到TGT,并檢查當(dāng)前用戶登錄過的所有service,并依次發(fā)送注銷請求。

2.5 CAS Client注銷過程

CAS Client的注銷,核心代碼是SingleSignOutFilter,它的關(guān)鍵代碼

public voiddoFilter(servletRequest, servletResponse, filterChain){

         HttpServletRequest request =(HttpServletRequest)servletRequest;

         if (handler.isTokenRequest(request)) {

                   handler.recordSession(request);

         } else if (handler.isLogoutRequest(request)) {

                   handler.destroySession(request);

                   return;

         }

         filterChain.doFilter(servletRequest, servletResponse);

}

其中handler是SingleSignOutHandler的實例,這個對象完成用戶在CASClient端登錄信息的維護和注銷工作。

至此,CAS完整的登錄和注銷過程就完成。

2.6 思考:什么情況統(tǒng)一注銷會失敗

統(tǒng)一注銷的實現(xiàn),需要CAS Server通過HttpClient訪問CAS Client的service。如果這個訪問過程失敗,就會導(dǎo)致統(tǒng)一注銷失敗。列了幾種情況,不詳述。

1、開發(fā)調(diào)試階段,使用localhost訪問CAS Client。

2、CAS Server部署在外網(wǎng),CAS Client部署在內(nèi)網(wǎng)。

3、網(wǎng)絡(luò)安全設(shè)置,不允許CASServer訪問CAS Client。

 

3 CAS Client集群的影響

前面的論述,一直假定所有的CAS Client都是單點部署,沒有集群。如果集群,會有什么影響,應(yīng)該如何來解決?

3.1 Client集群對登錄的影響

假設(shè)使用nginx做集群前端,后面部署兩臺CAS Client 01的實例。我們看看對登錄過程會有什么影響。

為了描述方便,CAS Client登錄過程會有三次請求(對應(yīng)三個過濾器),我們依次命名為Authentication Request / Validation Request / Wrapper Request。

Nginx缺省的分發(fā)規(guī)則,并不是sticky模式,同一個瀏覽器的請求,會按照nginx自身某種規(guī)則進行分發(fā)。我們曾經(jīng)測試過,在雙點集群環(huán)境下,Authentication Request和ValidationRequest會恰好被分發(fā)到兩臺服務(wù)器,這就會導(dǎo)致登錄過程死循環(huán)。

出現(xiàn)登錄死循環(huán)的原因,主要在于nginx分發(fā)時,沒有使用sticky策略,也就是同一個瀏覽器的請求,永遠分發(fā)給同一臺CAS Client實例。缺省nginx的分發(fā)策略,可以根據(jù)用戶IP分發(fā),實現(xiàn)的是同一個IP永遠分發(fā)到同一臺Client,這樣就能解決死循環(huán)的問題。

3.2 Client集群對注銷的影響

當(dāng)nginx實現(xiàn)了sitcky轉(zhuǎn)發(fā),同一個瀏覽器的訪問會分發(fā)到同一個Client1實例,該用戶的會話信息也一直保存在Client1實例中。

當(dāng)用戶統(tǒng)一注銷時,由CAS Server向Client發(fā)送注銷請求,這時候nginx無法確保按當(dāng)前用戶進行分發(fā),因此可能會被分發(fā)到Client2。這時候,實際效果是注銷失敗。

這個問題,在我們當(dāng)前的環(huán)境中真實存在,還沒有合理的解決方法。初步分析,大概有幾個修改方向。

3.2.1  修改nginx分發(fā)策略

問題存在的原因,是因為nginx在分發(fā)注銷策略時,不能準(zhǔn)確分發(fā)。如果能在這個環(huán)節(jié)進行修改,系統(tǒng)代碼和環(huán)境,基本不用做任何修改。

 

這里有兩種分發(fā)方法:

l CAS Server發(fā)送的注銷請求,分發(fā)給對應(yīng)的后臺服務(wù)器。

l CAS Server發(fā)送的注銷請求,廣播到所有的后臺服務(wù)器。

初步結(jié)論:同架構(gòu)組進行了溝通,這兩種方案都很難實現(xiàn),特別是廣播的方案,沒在網(wǎng)絡(luò)上找到類似成功的案例。

3.2.2  集群的節(jié)點實現(xiàn)Session同步

如果能實現(xiàn)集群Session的同步:同步創(chuàng)建、同步注銷,主要在一個Client上實現(xiàn)了注銷,其他Client也就同步注銷。

這個會對Tomcat性能有影響。

3.2.3  集群節(jié)點使用redis保存會話信息

即使是多個節(jié)點,它們的會話信息只有一份。一旦失效,則所有節(jié)點都失效。這只是一個設(shè)想,沒有做技術(shù)調(diào)研,不知能夠?qū)崿F(xiàn)。

 

這有兩種修改方法:

l 修改Tomcat的配置文件,使用redis保存Tomcat的會話信息。

l 修改代碼而不是Tomcat,使用redis保存會話信息。

初步結(jié)論:架構(gòu)組不允許修改生產(chǎn)環(huán)境的Tomcat,否定了第一種方法。我們只能嘗試修改代碼并利用redis保存會話。

3.2.4  每次請求驗證用戶是否注銷

首先,在CAS Server中實現(xiàn)一個接口,用于判斷某一個ST對應(yīng)的TGT是否還有效。

在SingleSignOutFilter中,每次訪問都調(diào)用CAS Server的這個新接口,判斷用戶是否已經(jīng)注銷。如果已經(jīng)注銷,則立刻注銷本實例中的會話信息。

這個方法是比較安全的解決辦法,但每次請求都會調(diào)用CASServer接口,會對性能造成巨大影響。完全不建議用這種方案。

 

3.2.5  幾種策略的初步調(diào)研

對前面提到的幾種方案做了初步調(diào)研之后:

l 技術(shù)實現(xiàn)困難,否定了方案1

l 性能考慮以及架構(gòu)組的策略,否定方案2

l 架構(gòu)組的策略,否定方案3中的第一種做法。

l 性能考慮,否定方案4。

因此,可能的做法是修改代碼,使用redis保存會話信息。

四  使用redis保存會話

在目前的生產(chǎn)環(huán)境的限制下,我們只能采用修改代碼來實現(xiàn)redis保存會話的實現(xiàn)方案。

4.1 Request和Session缺省怎么實現(xiàn)

在Tomcat缺省的實現(xiàn)中,Session信息都是保存在JVM中,所以不能跨JVM共享。

要想將所有的session都保存到redis中,一種能想到的簡單辦法是自己寫一個CustomSession,將會話信息保存到這個自定義的Session中,并且利用redis等進行保存。但這樣做,會帶來很大的代碼改動,所有涉及到session讀寫操作的地方可能都需要修改。

我們希望找到更優(yōu)雅的解決方案,能夠修改更少的代碼。

4.2 WEB請求的執(zhí)行過程

Request 和Session什么時候創(chuàng)建?如何傳遞?

Filter的調(diào)用入口函數(shù)是doFilter,傳入的主要參數(shù)是request和response。在此之前,Tomcat已經(jīng)創(chuàng)建好request。通常情況下,業(yè)務(wù)代碼不需要關(guān)心request和session等對象如何創(chuàng)建的問題,只需要使用即可。每個過濾器的實現(xiàn),當(dāng)需要繼續(xù)流程的時候,只需要將得到的request和response傳遞給下一個filter就行。

但這僅僅是缺省做法,并不表示我們不能修改或重寫一個request對象。我們想修改Session的保存位置,如果能在所有的Filter之前插入一個自定義過濾器,定義一個新的Request傳遞給后面的Filter,并且讓后面的Filter和Servlet感受不到變化,就可以實現(xiàn)這個目標(biāo)。

4.3 如何定制Request

4.3.1  增加過濾器

在所有的Filter之前,插入一個新的Filter。

HttpServletRequest可以重寫嗎?

4.3.2  Tomcat的Request實現(xiàn)

4.3.3  改寫之后的Request實現(xiàn)

在Session重寫一個RedisSessionRequest,繼承自HttpServletRequestWrapper,并包含原request(RequestFacade)的引用。但需要讀取Form參數(shù)時,直接調(diào)用oriRequest取值。當(dāng)需要拿到Session對象進行會話信息訪問時,調(diào)用重載后的函數(shù)。

這樣就實現(xiàn)了request的封裝,在后續(xù)的filter和servlet中通過request獲取到的session,都是放在redis中的會話數(shù)據(jù),不再是缺省保存在JVM中的數(shù)據(jù)。

4.3.4  集群環(huán)境的session讀寫

當(dāng)nginx將同一個瀏覽器的請求分發(fā)給不同的Tomcat時,都會根據(jù)SessionId從redis中讀取Session。因為同一個瀏覽器發(fā)送請求的SessionID相同,所以在不同的Tomcat實例中,會讀取到同一個Session對象。

4.4 使用Spring Session實現(xiàn)

根據(jù)前面的分析,在項目中自定義Request,就可以實現(xiàn)需求。Spring Session已經(jīng)是一個成熟的開源實現(xiàn),并且后端實現(xiàn)了將會話保存在redis、mongodb、jdbc等多種實現(xiàn),我們沒必要自己發(fā)明輪子。

Spring提供的例子代碼很簡潔,跟我們已經(jīng)實現(xiàn)的業(yè)務(wù)系統(tǒng)稍微有點不同。在現(xiàn)有系統(tǒng)中,已經(jīng)定義了bean jedisConnectionFactory,可以直接使用。

4.4.1  修改pom.xml

在pom.xml文件中,添加代碼

<dependency>

         <groupId>org.springframework.session</groupId>

         <artifactId>spring-session-data-redis</artifactId>

         <version>1.2.0.RELEASE</version>

</dependency>

4.4.2  修改redis配置文件

在項目中已經(jīng)有redis配置文件spring-redis.xml,在其中添加內(nèi)容

<context:annotation-config/>

<beans:beanclass="org.springframework.session.data.redis.config.annotation.web.http.RedisHttpSessionConfiguration"/>

4.4.3  修改web.xml

在所有的過濾器前面添加一個新的過濾器

<filter>

 <filter-name>springSessionRepositoryFilter</filter-name>

 <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>

</filter>

<filter-mapping>

 <filter-name>springSessionRepositoryFilter</filter-name>

  <url-pattern>/*</url-pattern>

  <dispatcher>REQUEST</dispatcher>

  <dispatcher>ERROR</dispatcher>

</filter-mapping>

4.5 測試實現(xiàn)效果

集成Spring Session后,經(jīng)過初步測試,能夠達到預(yù)想效果。(感謝同事瑞釗的實際測試并提供截圖)

4.5.1  Session信息已經(jīng)保存到redis中

用戶登錄后查看redis中的數(shù)據(jù),可以看到這些Session信息。

4.5.2  刪除redis中會話的影響

用戶登錄后繼續(xù)訪問系統(tǒng),不會切換到CAS登錄頁面。

如果手工刪掉redis中的session,重新訪問,可以看到需要重新做一個CAS認證的過程。

4.5.3  統(tǒng)一用戶注銷的測試

后續(xù)需要部署一套生產(chǎn)環(huán)境的集群環(huán)境,驗證統(tǒng)一注銷的效果。經(jīng)過前面兩步測試驗證,理論上說注銷已經(jīng)不是問題。

本站僅提供存儲服務(wù),所有內(nèi)容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊舉報。
打開APP,閱讀全文并永久保存 查看更多類似文章
猜你喜歡
類似文章
CAS干單點登陸(SSO)
cas客戶端集群無法登出問題
Javaweb重要知識點總結(jié)(三)Cookie 和 Session
SSO單點登錄系列2:cas客戶端和cas服務(wù)端交互原理動畫圖解,cas協(xié)議終極分析
CAS實現(xiàn)SSO單點登錄原理
使用 CAS 在 Tomcat 中實現(xiàn)單點登錄
更多類似文章 >>
生活服務(wù)
分享 收藏 導(dǎo)長圖 關(guān)注 下載文章
綁定賬號成功
后續(xù)可登錄賬號暢享VIP特權(quán)!
如果VIP功能使用有故障,
可點擊這里聯(lián)系客服!

聯(lián)系客服