CAS(Central Authentication Service)框架是一個開源的單點登錄框架。 最近公司要將幾個產品實現單點登錄,同事在負責技術篩選時選擇了CAS,並已實現了單點登錄功能,可是在配置代理功能時,遇到了諸多問題,目前雖然代理勉強可用,但問題重重,好比系統A在後臺須要訪問系統B的功能,在此情景中,系統A就至關於代理應用,而系統B是被代理的應用。 java
問題以下:
1.系統A做爲代理,不能向被代理應用系統B發送POST方式的HTTP請求。
2.系統A向系統B發送GET請求時,不能以URL附加的方式傳參數(形如 http://myhost.com:8080/myapp?name=abc),只能以RESTFUL風格傳參數(形如:http://myhost.com:8080/myapp/abc)。
3.測試期間,應用服務器重啓後,偶爾出現系統A代理失效的狀況,此時須要註銷用戶,從新登錄。 web
如上種種,極大地限制了CAS單點登錄的靈活性及穩定性。據瞭解,同事在實現代理功能時,稍微走了偏門,其大概實現方式以下: spring
1.在CAS驗證服務器後臺用戶表中增長一個字段castgc,當用戶驗證經過時,可用於保存cookie數據tgc(Ticket Granting Cookie)。
2.當用戶瀏覽器訪問系統A時,被重定向至CAS服務器進行驗證,正確輸入賬密後經過驗證,服務器後臺保存castgc字段值至用戶表中,同時CAS客戶端系統A將此castgc保存至session中,以備代理時使用。
3.系統A以代理身份訪問系統B時,會從session中取出castgc,放入請求頭(request.setHeader("Cookie",castgc);)。而後系統B接收到此請求,會拿着此Cookie到CAS服務器驗證,驗證經過後,便重定向至系統B,讓其響應代理即系統A,整個請求處理即告完成。 瀏覽器
OK,回到主題,我將借鑑網上的部分資料並以最接近官方的方式和最少的配置實現CAS代理功能。在開始以前,咱們先給本地機器作些小小的改動,找到本地系統中的文件C:\WINDOWS\system32\drivers\etc\hosts,在末尾加上: tomcat
########### 127.0.0.1 cas-server.test.com 127.0.0.1 cas-client1.test.com 127.0.0.1 cas-client2.test.com ###########
目的是爲了讓咱們便於區別cas服務端與各個客戶端的url,同時加深理解。 服務器
1.下載CAS服務端和客戶端的最新版本: cookie
CAS服務端:http://downloads.jasig.org/cas/cas-server-3.5.2-release.zip CAS客戶端:http://downloads.jasig.org/cas-clients/cas-client-3.2.1-release.zip
2.部署服務端: 首先將它們所有解壓。先看服務端,解壓後的目錄中找到cas-server-3.5.2\modules\cas-server-webapp-3.5.2.war,這就是打包後的服務端程序,把它拷出來重命名爲cas-server,直接丟進tomcat中就能夠在運行時被自動解壓並訪問了。(注意訪問的URL,就是前面配置過的) 網絡
因爲本文不使用https方式訪問,爲了使http方式的單點登錄功能生效,必須對工程中的文件\WEB-INF\spring-configuration\ticketGrantingTicketCookieGenerator.xml作一點修改,將其中的p:cookieSecure="true"改成p:cookieSecure="false",便可實現http單點登錄。但還要再配置\WEB-INF\deployerConfigContext.xml,在 session
<bean class="org.jasig.cas.authentication.handler.support.HttpBasedServiceCredentialsAuthenticationHandler" p:httpClient-ref="httpClient" />
中增一個屬性 p:requireSecure="false",不然後面的代理票據沒法生成,也就沒法實現代理功能。 app
3.構建客戶端工程cas-client1: 客戶端的工程也很簡單,用eclipse建立一個Dynamic Web Project,命名爲cas-client1,而後作點改動。 a.將解壓後目錄中的\cas-client-3.2.1\modules\cas-client-core-3.2.1.jar拷到cas-client1工程的lib目錄下。
b.將官方的4個filter拷到工程的web.xml文件中,參考官方文檔: https://wiki.jasig.org/display/CASC/Configuring+the+Jasig+CAS+Client+for+Java+in+the+web.xml ,並本身加上對應的filter-mapping(完整配置會在文後貼出),並將其中的全部filter配置中init-param對應的url進行相應的修改,如:
<filter> <filter-name>CAS Authentication Filter</filter-name> <filter-class>org.jasig.cas.client.authentication.AuthenticationFilter</filter-class> <init-param> <param-name>casServerLoginUrl</param-name> <param-value>http://cas-server.test.com:8080/cas-server/login</param-value> </init-param> <init-param> <param-name>serverName</param-name> <param-value>http://cas-client1.test.com:8080</param-value> </init-param> </filter> <filter> <filter-name>CAS Validation Filter</filter-name> <filter-class>org.jasig.cas.client.validation.Cas20ProxyReceivingTicketValidationFilter</filter-class> <init-param> <param-name>casServerUrlPrefix</param-name> <param-value>http://cas-server.test.com:8080/cas-server/</param-value> </init-param> <init-param> <param-name>serverName</param-name> <param-value>http://cas-client1.test.com:8080</param-value> </init-param> </filter>
<filter-mapping> <filter-name>CAS Validation Filter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <filter-mapping> <filter-name>CAS Authentication Filter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>c.如上是官方文檔中的配置,除此以外,咱們要實現代理,還要在validation filter中加入兩個參數,以下:
<init-param> <param-name>proxyCallbackUrl</param-name> <param-value>http://cas-client1.test.com:8080/cas-client1/proxyCallback</param-value> </init-param> <init-param> <param-name>proxyReceptorUrl</param-name> <param-value>/proxyCallback</param-value> </init-param>加入filter-mapping供後面的被代理應用cas-client2回調時使用,以下:
<filter-mapping> <filter-name>CAS Validation Filter</filter-name> <url-pattern>/proxyCallback</url-pattern> </filter-mapping>d.寫一個自定義的servlet,並配置到web.xml中。核心代碼以下:
public class HelloWorldExample extends HttpServlet { private static final long serialVersionUID = 1L; @Override public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { // ........ HttpSession session = request.getSession(); final Assertion assertion = (Assertion) (session == null ? request .getAttribute(AbstractCasFilter.CONST_CAS_ASSERTION) : session .getAttribute(AbstractCasFilter.CONST_CAS_ASSERTION)); // 被代理應用的URL String serviceUrl = "http://cas-client2.test.com:8080/cas-client2/HelloWorldExample"; final String proxyTicket = assertion.getPrincipal().getProxyTicketFor( serviceUrl); URL url = new URL(serviceUrl + "?ticket=" + proxyTicket);// 不須要cookie,只需傳入代理票據 HttpURLConnection conn = null; conn = (HttpURLConnection) url.openConnection(); // conn.setRequestMethod("POST");//使用POST方式 conn.setDoOutput(true); OutputStreamWriter osw = new OutputStreamWriter(conn.getOutputStream(), "UTF-8"); osw.write("name=阿J小蟲abc");// 傳參 osw.close(); // 業務處理 // ........ } }web.xml加入以下:
<!-- my filter --> <servlet> <servlet-name>HelloWorldExample</servlet-name> <servlet-class>HelloWorldExample</servlet-class> </servlet> <servlet-mapping> <servlet-name>HelloWorldExample</servlet-name> <url-pattern>/HelloWorldExample</url-pattern> </servlet-mapping>
到此爲止,咱們的第一個客戶端工程cas-client1構建完畢,將它與cas-server一塊兒部署到tomcat並運行,便可看到單點登錄的效果(用戶名和密碼相同便可經過驗證)。
4.構建客戶端工程cas-client2: 將cas-client1複製出來重命名爲cas-client2。具體來說,做爲被代理應用,它主要是用來從cas-client1獲取請求參數name=阿J小蟲,而後響應代理(輸出數據)。String name = request.getParameter("name"); if (name != null) System.out .println(new String(name.getBytes("iso-8859-1"), "utf-8")); PrintWriter out = response.getWriter(); out.print("congratulation!you got it from cas-client2!");另外,被代理應用也能夠做爲第三個客戶端的代理應用,但在此不做考慮,因此能夠註釋掉validation filter中的參數proxyCallbackUrl和proxyReceptorUrl,並加入另外兩個參數redirectAfterValidation和acceptAnyProxy,以下:
<filter> <filter-name>CAS Validation Filter</filter-name> <filter-class>org.jasig.cas.client.validation.Cas20ProxyReceivingTicketValidationFilter</filter-class> <init-param> <param-name>casServerUrlPrefix</param-name> <param-value>http://cas-server.test.com:8080/cas-server/</param-value> </init-param> <init-param> <param-name>serverName</param-name> <param-value>http://cas-client2.test.com:8080</param-value> </init-param> <!-- <init-param> <param-name>proxyCallbackUrl</param-name> <param-value>http://cas-client2.test.com:8080/cas-client2/proxyCallback</param-value> </init-param> <init-param> <param-name>proxyReceptorUrl</param-name> <param-value>/proxyCallback</param-value> </init-param> --> <init-param> <!-- redirectAfterValidation must be false, otherwise the request params from proxying app could not be received --> <param-name>redirectAfterValidation</param-name> <param-value>false</param-value> </init-param> <init-param> <param-name>acceptAnyProxy</param-name> <param-value>true</param-value> </init-param> </filter>
最後記得將拷過來的web.xml文件中的相關url改爲與cas-client2對應的。與cas-server和cas-client1一同部署,登錄cas-client1代理程序時,後臺成功實現對cas-client2的訪問並獲取返回數據。
注意事項:
1.當用eclipse進行調試時,如把斷點設置在獲取響應以前,可能會出現異常,以下:
三月 21, 2013 3:00:30 下午 org.jasig.cas.client.validation.AbstractTicketValidationFilter doFilter WARNING: org.jasig.cas.client.validation.TicketValidationException: 未可以識別出目標 'ST-2-cMeR2w5xzryNeSuh3jx9-cas01.example.org'票根
一開始我也覺得票根有問題,此時,只要取消斷點從新登錄一下就能順利響應了,至於爲何設置斷點就報錯,多是網絡的響應被斷點中斷從而延時了致使的。
2.被代理應用cas-client2中的validation filter參數redirectAfterValidation必須設置爲false,不然接收不到代理傳過來的請求參數,如name=阿J小蟲。還有參數acceptAnyProxy與allowedProxyChains二選一,若是配置acceptAnyProxy,必須爲true,本文示例使用此參數。
示例工程下載:
請到做者博客原文末尾處下載:
http://www.ichatter.cn/2013/03/21/385/