一.問題在哪?
在配置cas-client中,有這么一段配置:
<filter> <filter-name>CAS Filter</filter-name> <filter-class>org.jasig.cas.client.authentication.AuthenticationFilter</filter-class> <init-param> <param-name>casServerLoginUrl</param-name> <param-value>https://demo.testcas.com/cas/login</param-value> </init-param> <init-param> <param-name>serverName</param-name> <param-value>http://app1.testcas.com</param-value> </init-param> </filter> <filter-mapping> <filter-name>CAS Filter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
詳情請參考:單點登錄CAS使用記(二):部署CAS服務器以及客戶端
意思大概是:攔截客戶端的所有請求,如果發現還沒有通過CAS認證中心認證,則強行重定向到Cas-server的登錄頁面。
這里面有一個問題?
即:客戶端所有請求都被攔截並且跳轉,包括靜態資源,靜態頁面等,這是不合理的。
二.如何解決?
2.1 百度下大多數的解決方案
擴展CASFilter過濾器,加上一個排除指定URL的功能,然后在web.xml配置中,手動添加需要排除的所有URL。
這確是一種解決方法,但是過於繁瑣,而且在網站請求地址過多的情況下,稍有不慎,就會出現遺漏。
如果這是你想要的,請另行百度。
2.2 我的解決方案:
因為我是要整合兩個比較成熟的項目,換言之,就是這兩個項目已經自帶了用戶登錄驗證、用戶權限驗證、不攔截靜態資源等處理。
所以我是不是可以利用原有項目的攔截邏輯?
原有項目功能簡述:
1.項目采用了SpringMVC框架
2.對所有靜態資源放行,例如:css、js、img等
例如:web.xml中配置如下(激活Tomcat的defaultServlet來處理靜態文件)
<servlet-mapping> <servlet-name>default</servlet-name> <url-pattern>*.jpg</url-pattern> </servlet-mapping> <servlet-mapping> <servlet-name>default</servlet-name> <url-pattern>*.js</url-pattern> </servlet-mapping> <servlet-mapping> <servlet-name>default</servlet-name> <url-pattern>*.css</url-pattern> </servlet-mapping>3.自定義了一個@RequestSecurity的注解,並對所有有權限控制的方法加上該注解
4.用戶發出請求->被攔截器攔截->攔截器判斷該用戶請求方法是否被標注了@RequestSecurity
IF:如果有該注解,繼續判斷用戶是否已經登錄(通過session是否存在來判斷),如果沒有登錄,則重定向到登錄頁面進行登錄。
ELSE:如果沒有該注解,證明該資源可隨意訪問,直接放行。
注意上文紅色描述,所以,我只要稍微改造一下原有項目的攔截器,讓他不是直接跳轉到原有登錄頁面,跳轉到一個特定的請求地址,讓cas-filter攔截只攔截這一個請求就可以了。
三.改造驗證
第一步:修改原有項目攔截器
項目中,有這么一段SpringMVC攔截器配置(偽代碼展示)
<mvc:interceptors> <bean class="com.xxxx.interceptor.SecurityInterceptor"> <property name="redirectUrl"> <value>/login</value> </property> ... </bean> </mvc:interceptors>
意思就是,一旦攔截到用戶未登錄,直接跳轉到doLogin方法,讓用戶登錄。
我對他修改如下:
<mvc:interceptors> <bean class="com.xxxx.interceptor.SecurityInterceptor"> <property name="redirectUrl"> <value>/casLogin</value> </property> ... </bean> </mvc:interceptors>
意思就是:一旦攔截到用戶未登錄,跳轉到doCasLogin方法
第二步:新增casLogin方法
@RequestSecurity @RequestMapping(value = "casLogin", method = { RequestMethod.GET, RequestMethod.POST }) public String casLogin() { return "welcome"; }
意思就是:如果用戶未登錄,會被強制重定向到本方法。
第三步:修改cas-client的攔截器Cas-Filter的攔截范圍
<filter> <filter-name>CAS Filter</filter-name> <filter-class>org.jasig.cas.client.authentication.AuthenticationFilter</filter-class> <init-param> <param-name>casServerLoginUrl</param-name> <param-value>https://demo.testcas.com/cas/login</param-value> </init-param> <init-param> <param-name>serverName</param-name> <param-value>http://app1.testcas.com</param-value> </init-param> </filter> <filter-mapping> <filter-name>CAS Filter</filter-name> <url-pattern>/casLogin</url-pattern> </filter-mapping>
意思就是:CAS Filter只攔截casLogin請求,其他請求一律不攔截。一旦攔截到casLogin請求,說明用戶暫未登錄,則強制重定向到Cas-Server的登錄頁面。
到這里,Cas-client不攔截靜態資源處理就改造好了。
但是,如果只做上面3步,進行測試的話,瀏覽器有可能會報“此頁面包含重定向循環”的錯誤提示,並不能真正登入頁面。
原因看以下流程:
1.首先被重定向到了Cas認證中心
2.用戶輸入用戶名密碼等,點擊登錄通過了Cas登陸認證
3.再一次被重定向回/casLogin,
4.又被Cas-Fliter攔截,不過此時Cas-Fliter發現用戶已經通過了Cas認證中心認證,不做重定向處理,繼續后續處理。
5.因為casLogin方法標注了@RequestSecurity,所以又被原有項目的mvc:interceptors攔截,攔截器發現用戶未登錄(通過session是否為空來判斷)
6.又被強行重定向會casLogin,然后又回到了4,陷入了無窮的循環中...
如何打破此重定向循環?
關鍵在於第5步,當Cas-Server重定向回來時,會帶回通過認證的用戶信息。
取得方式如下:
AttributePrincipal principal = (AttributePrincipal) request.getUserPrincipal();
String username = principal.getName();
這時,在過濾器判斷用戶是否已經登錄前,先行判斷如果是Cas-server重定向過來的請求,並且principal、username存在,則把用戶信息寫入客戶端UserSession中。
然后再判斷發現,已經寫入了UserSession了,說明登錄成功,直接登錄welcome頁面。
登錄成功。
注:本文所記錄的是自己摸索所得,並不敢完全保證程序邏輯的嚴謹性,如果您發現有所紕漏,請給予批評指正。
單點登錄CAS使用記系列:
-
單點登錄CAS使用記(一):前期准備以及為CAS-Server配置SSL協議
-
單點登錄CAS使用記(二):部署CAS服務器以及客戶端
-
單點登錄CAS使用記(三):實現自定義驗證用戶登錄
-
單點登錄CAS使用記(四):為登錄頁面加上驗證碼
-
單點登錄CAS使用記(五):cas-client不攔截靜態資源以及無需登錄的請求。
-
單點登錄CAS使用記(六):單點登出、單點注銷
-
單點登錄CAS使用記(七):關於服務器超時以及客戶端超時的分析
-
單點登錄CAS使用記(八):使用maven的overlay實現無侵入的改造CAS