工作中實際遇到的需求,我們有一個舊系統,用了CAS的單點登錄,現在有一個外部系統,準備從它那裡單點進來,這個外部系統提供了一個token參數來標記這是哪一個用戶,我們用他們提供的方式解析出對應的用戶,以這個用戶從CAS登錄進系統。 有關CAS登錄的分析網上多如牛毛,這裡不准備多作分析了,直接上解決過 ...
工作中實際遇到的需求,我們有一個舊系統,用了CAS的單點登錄,現在有一個外部系統,準備從它那裡單點進來,這個外部系統提供了一個token參數來標記這是哪一個用戶,我們用他們提供的方式解析出對應的用戶,以這個用戶從CAS登錄進系統。
有關CAS登錄的分析網上多如牛毛,這裡不准備多作分析了,直接上解決過程。
這裡實現是基於我們以前系統的,是CAS 3.5.2- 首先在登錄流程文件login-webflow.xml里在on-start節點後面插入
<decision-state id="tokenCheck"> <if test="requestParameters.token != null and requestParameters.token != ''" then="tokenValidate" else="ticketGrantingTicketExistsCheck" /> </decision-state>
在這裡檢查是否有token參數,有的話執行token驗證,沒有的話走正常流程,ticketGrantingTicketExistsCheck就是原有的正常流程。
- 定義token驗證節點
<action-state id="tokenValidate"> <evaluate expression="tokenLoginAction.doExecute(flowRequestContext)" /> <transition on="error" to="generateLoginTicket" /> <transition on="success" to="sendTicketGrantingTicket" /> </action-state>
失敗則走generateLoginTicket分支,會生成一個LT,並重定向到登錄頁面,這也是通常頁面登錄失敗後的路徑
成功則走sendTicketGrantingTicket分支,即頁面正常登錄成功時走的路徑
- 實現token驗證流程節點
在cas-servlet.xml里添加添加tokenLoginAction Bean
<bean id="tokenLoginAction" class="org.jasig.cas.web.flow.TokenLoginAction" p:centralAuthenticationService-ref="centralAuthenticationService" />
實現TokenLoginAction
這裡主要解析token,並生成TGT。主要代碼如下:
HttpServletRequest request = WebUtils.getHttpServletRequest(context); String token = request.getParameter("token"); try { //解析Token,略。。。。。。 CasCredentials credentials = new CasCredentials(); credentials.setUsername(userName); credentials.setPassword(""); credentials.setNoAuth(true); String tgt = centralAuthenticationService.createTicketGrantingTicket(credentials); WebUtils.putTicketGrantingTicketInRequestScope(context,tgt); } catch (Exception e) { e.printStackTrace(); return "error"; } return "success";TokenLoginAction的主要邏輯代碼
上面centralAuthenticationService是註入的屬性,
CasCredentials則是繼承自UsernamePasswordCredentials的一個自定義Credentials,在用戶名、密碼基礎上添加了一個noAuth屬性,用來標記是不是需要驗證密碼。這裡由外系統提供的token保證安全性,把noAuth設為true。
而登錄驗證邏輯在createTicketGrantingTicket這個方法里,驗證未通過會拋出異常。
真正驗證的地方則是在authenticationManager里,裡面有authenticationHandlers定義了驗證方法- 修改登錄驗證邏輯
login-webflow.xml頂部把credentials的定義先改了
<var name="credentials" class="com.cas.util.CasCredentials" />
deployerConfigContext.xml找到自定義登錄驗證所在
<bean id="authenticationManager" class=""> <property name="authenticationHandlers"> <list> <bean class="com.cas.util.QueryUserAuthenticationHandler"> ...... </bean> </list> </property> </bean>
在這個QueryUserAuthenticationHandler class里,驗證密碼之前加入
if (CasCredentials.class.isInstance(credentials)) { if (((CasCredentials)credentials).isNoAuth()) return true; }
這樣就完成了傳入第三方token的CAS登錄。
似乎是完成了,但其實還有一些東西,
比如第三方進來的時候是不用他們傳Service這個參數的,而這個參數是在CAS登錄初始化時處理掉的,後面沒有地方自己往request裡加這個參數讓CAS來處理它,自己寫requestscope里寫Service對象又很麻煩,看了下代碼,得註入很多東西才行。這樣就在進入CAS流程前,自己往請求里塞一個Service參數,然後重定向到CAS登錄的url。
再比如,這次是別人提供Token用他們的方法解;以後有需求是我們提供一個Token出去,接收進來後用我們的方法自己解。所以其實TokenLoginAction那裡解析Token其實是解本方提供出去的Token。解別人的Token呢前置到塞Service參數那個地方,那裡解析出來用戶名後,再用自己的方法生成一個Token發給CAS。這樣就把第三方的Token解析分離出去了,CAS登錄的地方不會跟別人的實現綁在一起。