標籤:catch art tostring cli cookies pcl 通過 過程 initial
3.2.2具有SSO功能的web應用原始碼解析要實現WEB-SSO的功能,只有身份認證服務是不夠的。這點很顯然,要想使多個應用具有單點登入的功能,還需要每個應用本身的配合:將自己的身份認證的服務交給一個統一的身份認證服務-SSOAuth。SSOAuth服務中提供的各個方法就是供每個加入SSO的Web應用來調用的。一般來說,Web應用需要SSO的功能,應該通過以下的互動過程來調用身份認證服務的提供的認證服務:
- Web應用中每一個需要安全保護的URL在訪問以前,都需要進行安全檢查,如果發現沒有登入(沒有發現認證之後所帶的cookie),就重新定向到SSOAuth中的login.jsp進行登入。
- 登入成功後,系統會自動給你的瀏覽器設定cookie,證明你已經登入過了。
- 當你再訪問這個應用的需要保護的URL的時候,系統還是要進行安全檢查的,但是這次系統能夠發現相應的cookie。
- 有了這個cookie,還不能證明你就一定有許可權訪問。因為有可能你已經logout,或者cookie已經到期了,或者身份認證服務重起過,這些情況下,你的cookie都可能無效。應用系統拿到這個cookie,還需要調用身份認證的服務,來判斷cookie時候真的有效,以及當前的cookie對應的使用者是誰。
- 如果cookie效驗成功,就允許使用者訪問當前請求的資源。
以上這些功能,可以用很多方法來實現:
- 在每個被訪問的資源中(JSP或Servlet)中都加入身份認證的服務,來獲得cookie,並且判斷目前使用者是否登入過。不過這個笨方法沒有人會用:-)。
- 可以通過一個controller,將所有的功能都寫到一個servlet中,然後在URL映射的時候,映射到所有需要保護的URL集合中(例如*.jsp,/security/*等)。這個方法可以使用,不過,它的缺點是不能重用。在每個應用中都要部署一個相同的servlet。
- Filter是比較好的方法。符合Servlet2.3以上的J2EE容器就具有部署filter的功能。(Filter的使用可以參考JavaWolrd的文章http://www.javaworld.com/javaworld/jw-06-2001/jw-0622-filters.html)Filter是一個具有很好的模組化,可重用的編程API,用在SSO正合適不過。本範例就是使用一個filter來完成以上的功能。
package SSO; import java.io.*;import java.net.*;import java.util.*;import java.text.*;import javax.servlet.*;import javax.servlet.http.*;import javax.servlet.*;import org.apache.commons.httpclient.*;import org.apache.commons.httpclient.methods.GetMethod; public class SSOFilter implements Filter { private FilterConfig filterConfig = null; private String cookieName="WangYuDesktopSSOID"; private String SSOServiceURL= "http://wangyu.prc.sun.com:8080/SSOAuth/SSOAuth"; private String SSOLoginPage= "http://wangyu.prc.sun.com:8080/SSOAuth/login.jsp"; public void init(FilterConfig filterConfig) { this.filterConfig = filterConfig; if (filterConfig != null) { if (debug) { log("SSOFilter:Initializing filter"); } } cookieName = filterConfig.getInitParameter("cookieName"); SSOServiceURL = filterConfig.getInitParameter("SSOServiceURL"); SSOLoginPage = filterConfig.getInitParameter("SSOLoginPage"); } .......... }以上的初始化的原始碼有兩點需要說明:一是有兩個需要配置的參數SSOServiceURL和SSOLoginPage。因為當前的Web應用很可能和身份認證服務(SSOAuth)不在同一台機器上,所以需要讓這個filter知道身份認證服務部署的URL,這樣才能去調用它的服務。另外一點就是由於身份認證的服務調用是要通過http協議來調用的(在本範例中是這樣設計的,讀者完全可以設計自己的身份服務,使用別的調用協議,如RMI或SOAP等等),所有筆者引用了apache的commons工具包(詳細資料情訪問apache 的網站http://jakarta.apache.org/commons/index.html),其中的“httpclient”可以大大簡化http調用的編程。下面看看filter的主體方法doFilter():public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { if (debug) log("SSOFilter:doFilter()"); HttpServletRequest request = (HttpServletRequest) req; HttpServletResponse response = (HttpServletResponse) res; String result="failed"; String url = request.getRequestURL().toString(); String qstring = request.getQueryString(); if (qstring == null) qstring =""; //檢查http請求的head是否有需要的cookie String cookieValue =""; javax.servlet.http.Cookie[] diskCookies = request.getCookies(); if (diskCookies != null) { for (int i = 0; i< diskCookies.length; i++) { if(diskCookies[i].getName().equals(cookieName)){ cookieValue = diskCookies[i].getValue(); //如果找到了相應的cookie則效驗其有效性 result = SSOService(cookieValue); if (debug) log("found cookies!"); } } } if (result.equals("failed")) { //效驗失敗或沒有找到cookie,則需要登入 response.sendRedirect(SSOLoginPage+"?goto="+url); } else if (qstring.indexOf("logout") > 1) {//logout服務 if (debug) log("logout action!"); logoutService(cookieValue); response.sendRedirect(SSOLoginPage+"?goto="+url); } else {//效驗成功 request.setAttribute("SSOUser",result); Throwable problem = null; try { chain.doFilter(req, res); } catch(Throwable t) { problem = t; t.printStackTrace(); } if (problem != null) { if (problem instanceof ServletException) throw (ServletException)problem; if (problem instanceof IOException) throw (IOException)problem; sendProcessingError(problem, res); } } }doFilter()方法的邏輯也是非常簡單的,在接收到請求的時候,先去尋找是否存在期望的cookie值,如果找到了,就會調用SSOService(cookieValue)去效驗這個cookie的有效性。如果cookie效驗不成功或者cookie根本不存在,就會直接轉到登入介面讓使用者登入;如果cookie效驗成功,就不會做任何阻攔,讓此請求進行下去。在設定檔中,有下面的一個節點表示了此filter的URL映射關係:只攔截所有的jsp請求。<filter-mapping><filter-name>SSOFilter</filter-name><url-pattern>*.jsp</url-pattern></filter-mapping> 下面還有幾個主要的函數需要說明: private String SSOService(String cookievalue) throws IOException { String authAction = "?action=authcookie&cookiename="; HttpClient httpclient = new HttpClient(); GetMethod httpget = new GetMethod(SSOServiceURL+authAction+cookievalue); try { httpclient.executeMethod(httpget); String result = httpget.getResponseBodyAsString(); return result; } finally { httpget.releaseConnection(); } } private void logoutService(String cookievalue) throws IOException { String authAction = "?action=logout&cookiename="; HttpClient httpclient = new HttpClient(); GetMethod httpget = new GetMethod(SSOServiceURL+authAction+cookievalue); try { httpclient.executeMethod(httpget); httpget.getResponseBodyAsString(); } finally { httpget.releaseConnection(); } }這兩個函數主要是利用apache中的httpclient訪問SSOAuth提供的認證服務來完成效驗cookie和logout的功能。其他的函數都很簡單,有很多都是我的IDE(NetBeans)替我自動產生的。
web-sso-client