CAS的集群環境,包括CAS的客戶應用是集群環境,以及CAS服務本身是集群環境這兩種情況。在集群環境下使用CAS,要解決兩個問題,一是單點退出(注銷)時,CAS如何將退出請求正確轉發到用戶session所在的具體客戶應用服務器,而不是轉發到其他集群服務器上,二是解決CAS服務端集群環境下各種Ticket信息的共享。
由于CAS Server是一個Web應用,因此可部署在Tomcat等容器中。直接部署CAS集群并使用負載均衡配置后,由于每次訪問的CAS Server不固定,會發生通行證丟失。
解決方法:配置TOMCAT集群及Session復制,解決CAS Server Session復制。詳細配置方法見"Nginx+Tomcat+Memcached集群"。
當用戶登錄后,Ticket存儲在CAS Server中,由于這部分數據未保存在Session中,僅靠TOMCAT Session復制無法解決問題。默認配置下,CAS Server使用org.jasig.cas.ticket.registry.DefaultTicketRegistry把Ticket數據保存在 HashMap中,因此多臺CAS Server無法共享數據。導致用戶登錄及退出均存在問題。因此需要將Ticket信息進行共享存儲,使多臺CAS Server使用相同的存儲區域管理Ticket。
Ticket信息共享處理比較簡單,就是將Ticket信息從原來的內存存儲變為數據庫存儲。見"ticketRegistry.xml"文件。處理方法有兩種:1是將Ticket信息放在Redis內存數據庫中,2是將Ticket信息放在memcached中,推薦使用memcached,現在DMGeoSSO已經支持這兩種方式了,配置文件示例見"ticketRegistry.xml.redis"和ticketRegistry.xml.memcache。默認配置文件的內容為"ticketRegistry.xml.default"
Redis方式源代碼:RedisTicketRegistry.java
Memcached方式源代碼:MemCacheTicketRegistry.java
這里需要注意的是:TGT和ST的超時時間最大只能設置為30天(即2592000秒),多1秒都不行。這是Memcached的特性。
根據CAS Server工作流程,當收到Logout請求后,CAS Server會刪除自身存儲的有關當前用戶的所有Ticket票據,"問題二"的解決方法已經解決了多臺CAS Server刪除票據的問題。但隨后從CAS Server會發起HTTP POST請求到應用服務器,該請求中具備"logoutRequest"標志,應用服務器的SingleSignOutFilter接收到該請求后在應用服務器端進行用戶登出操作。該操作主要是將應用服務器端的CAS Client中保存的用戶Session數據失效,達到客戶端登出效果。即,對于CAS系統,必須Server端和Client均進行登出操作,用戶才會真正登出。cas退出采用的是異步操作,客戶端是否退出成功也不關心。
CAS Server的這個工作流程,在應用集群部署的情況下帶來一系列問題。由于應用服務器集群化,且一般會使用Session復制,當CAS Server向應用服務器發起Logout請求時,僅針對一臺服務器發起請求,導致應用服務器沒有全部退出,使得用戶使用登出操作時,有時可以退出,有時不能退出,用戶體驗很差。
解決方法:采用廣播方式,將單臺Tomcat收到的注銷請求廣播給集群環境下的所有節點,達到所有服務器都注銷的效果。核心代碼:DMGeoSSOClient中的CasSingleLogoutClusterFilter.java。
配置方式,將DMGeoSSOClient工程下的lib目錄下的jar包(servlet-api-2.3.jar除外)以及dist下的cas-client-core-3.1.3.jar包復制到集群環境所有Tomcat的lib目錄,并修改所有tomcat的web.xml,增加過濾器的配置:
<filter>
<filter-name>CAS SLO Cluster Filter</filter-name>
<filter-class>org.esco.cas.client.CasSingleLogoutClusterFilter</filter-class>
<init-param>
<param-name>clientHostName</param-name>
<param-value>127.0.0.1:8080</param-value>
</init-param>
<init-param>
<param-name>peersUrls</param-name>
<param-value>http://127.0.0.1:8080,http://127.0.0.1:8081</param-value>
</init-param>
</filter>
clientHostName是本Tomcat的 另外,在集群中的所有需要做單點登錄的應用中,web.xml中增加: <filter-mapping> <filter-name>CAS SLO Cluster Filter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> 注意:這個過濾器需要放在原單點注銷的過濾器之前才有效。
nginx配置:
nginx.conf:
upstream cluster {
server 127.0.0.1:8080;
server 127.0.0.1:8081;
}
PRoxy.conf:
server {
listen 8888;
server_name 127.0.0.1;
#access_log logs/access.log main;
proxy_connect_timeout 60s;
proxy_send_timeout 300s;
proxy_read_timeout 300s;
proxy_buffer_size 1024k;
proxy_buffers 4 1024k;
proxy_busy_buffers_size 1024k;
proxy_temp_file_write_size 1024k;
proxy_next_upstream error timeout invalid_header http_500 http_502 http_503 http_504 http_404;
proxy_max_temp_file_size 1024m;
location ~ ^/DMGeoPortal/ {
proxy_pass http://cluster;
proxy_set_header Host $host:$server_port;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header REMOTE-HOST $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
location ~ ^/DMGeoSSO/ {
proxy_pass http://cluster;
proxy_set_header Host $host:$server_port;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header REMOTE-HOST $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
Tomcat配置:
context.xml:
<Manager className="de.javakaffee.web.msm.MemcachedBackupSessionManager"
memcachedNodes="n1:172.16.254.69:11211,n2:172.16.254.70:11211"
sticky="false"
sessionBackupAsync="false"
sessionBackupTimeout="18000"
transcoderFactoryClass="de.javakaffee.web.msm.serializer.javolution.JavolutionTranscoderFactory"
copyCollectionsForSerialization="false"
/>
server.xml:
<Engine name="Catalina" defaultHost="localhost">
注意:當sticky為false時,不需要配置jvmRoute屬性,當sticky為true時,一定要配置jvmRoute屬性,且集群中所有tomcat的jvmRoute屬性均不一樣。sticky的屬性默認為true。在CAS服務器端集群和客戶端集群環境下,需要將sticky配置為false,這樣可以避免很多莫名其妙的session丟失問題。
Sticky 模式:tomcat session 為 主session, memcached 為備 session。Request請求到來時, 從memcached加載備 session 到 tomcat (僅當tomcat jvmroute發生變化時,否則直接取tomcat session);Request請求結束時,將tomcat session更新至memcached,以達到主備同步之目的。下面是sticky模式時響應的流程圖(圖片來源網絡):
Non-Sticky模式:tomcat session 為 中轉session, memcached1 為主 session,memcached 2 為備session。Request請求到來時,從memcached 2加載備 session 到 tomcat,(當 容器 中還是沒有session 則從memcached1加載主 session 到 tomcat, 這種情況是只有一個memcached節點,或者有memcached1 出錯時),Request請求結束時,將tomcat session更新至 主memcached1和備memcached2,并且清除tomcat session 。以達到主備同步之目的,如下是non-sticky模式的響應流程圖:(圖片來源網絡)。

requestUriIgnorePattern屬性不要設置。否則在CAS服務器端集群和客戶端集群環境下有很多問題(包括影響注銷功能,不能完全注銷),因為我們的單點登錄是對所有的資源進行攔截的,不需要設置requestUriIgnorePattern(URL忽略)屬性。
集群中所有Tomcat的lib下新增的包:
javolution-5.4.3.1.jar
memcached-2.6.jar
memcached-session-manager-1.5.1.jar
memcached-session-manager-tc6-1.5.1.jar
msm-javolution-serializer-1.5.1.jar
msm-kryo-serializer-1.5.1.jar
msm-serializer-benchmark-1.5.1.jar
msm-xstream-serializer-1.5.1.jar
nginx配置:
nginx.conf:
upstream dotnetcluster {
server 127.0.0.1:80;
server 127.0.0.1:81;
}
proxy.conf:
server {
listen 8888;
server_name 127.0.0.1;
#access_log logs/access.log main;
proxy_connect_timeout 60s;
proxy_send_timeout 300s;
proxy_read_timeout 300s;
proxy_buffer_size 1024k;
proxy_buffers 4 1024k;
proxy_busy_buffers_size 1024k;
proxy_temp_file_write_size 1024k;
proxy_next_upstream error timeout invalid_header http_500 http_502 http_503 http_504 http_404;
proxy_max_temp_file_size 1024m;
location ~ ^/DMGeoGlobeWeb/ {
proxy_pass http://dotnetcluster;
proxy_set_header Host $host:8888;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header REMOTE-HOST $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
location ~ ^/DMGeoMIS/ {
proxy_pass http://dotnetcluster;
proxy_set_header Host $host:8888;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header REMOTE-HOST $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
這里與Java應用稍有不同,注意上述紅色加粗部分端口號的配置,該端口號是你最終訪問集群服務器的端口號。
IIS配置:
將下列dll文件放在Web應用程序的bin目錄:
Enyim.Caching.dll
MemcachedSessionProvider.dll
修改Web應用程序的Web.config配置:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<configSections>
<sectionGroup name="sessionManagement">
<section name="memcached" type="Enyim.Caching.Configuration.MemcachedClientSection, Enyim.Caching" />
</sectionGroup>
</configSections>
<sessionManagement>
<memcached protocol="Binary">
<servers>
<!-- make sure you use the same ordering of nodes in every configuration you have -->
<add address="172.16.254.69" port="11211" />
<add address="172.16.254.70" port="11211" />
</servers>
<locator type="MemcachedSessionProvider.SessionNodeLocator, MemcachedSessionProvider" />
</memcached>
</sessionManagement>
<system.web>
<sessionState customProvider="Memcached" mode="Custom">
<providers>
<add name="Memcached" type="MemcachedSessionProvider.SessionProvider, MemcachedSessionProvider" />
</providers>
</sessionState>
<machineKey validationKey="3A2122BF7DA69398B43FF26BD658CE428EC417BA" decryptionKey="5C90C7D3BE69792117E02AE72DDDAFBA853F5FDB3E57BC5C" decryption="3DES" validation="SHA1"/>
</system.web>
</configuration>
上述紅色加粗部分是新增的配置項。說明如下:
sessionManagement:用來配置Memcached連接地址。
sessionState:用將配置將Session存儲在什么地方,這里配置的是自定義,即將Session存儲在Memcached中。
machineKey:這是比較關鍵的配置。如果我們的Web應用程序是在同一個IIS服務器上,用不同的端口來區分不同的網站應用,那么不需要配置machineKey;如果我們的Web應用程序是在不同的IIS服務器上,那么切記一定要配置machineKey。machineKey是對密鑰進行配置,以便將其用于對 Forms 身份驗證 Cookie 數據和視圖狀態數據進行加密和解密,并將其用于對進程外會話狀態標識進行驗證。默認情況下,asp.net的配置是自己動態生成,如果單臺服務器當然沒問題,但是如果多臺服務器負載均衡,machineKey還采用動態生成的方式,每臺服務器上的machinekey值不一致,就導致加密出來的結果也不一致,不能共享驗證和 ViewState,所以對于多臺服務器負載均衡的情況,一定要在每個站點配置相同的machineKey。
machineKey的生成算法如下:
private static string CreateKey(int len)
{
byte[] bytes = new byte[len];
new RNGCryptoServiceProvider().GetBytes(bytes);
StringBuilder sb = new StringBuilder();
for (int i = 0; i < bytes.Length; i++)
{
sb.Append(string.Format("{0:X2}", bytes[i]));
}
return sb.ToString();
}
調用:
string validationKey = CreateKey(20);
string decryptionKey = CreateKey(24);
string machineKey = string.Format("<machineKey validationKey=/"{0}/" decryptionKey=/"{1}/" decryption=/"3DES/" validation=/"SHA1/"/>",validationKey, decryptionKey);
新聞熱點
疑難解答