單點登陸(SSO):SSO是指在多個應用系統中個,用戶只須要登錄一次就能夠訪問全部相互信任的應用系統。它包括能夠將此次主要的登陸映射到其餘應用中用於同一用戶的登錄的機制。跨域
SSO的實現過程:服務器
經過上述圖形,咱們能夠看到SSO的大致實現步驟主要分爲兩大步:存儲登陸信息,查驗登陸信息。cookie
對於SSO,咱們也能夠將之分爲兩大不一樣的類型:同域SSO和跨域SSO;其中同域SSO又能夠分爲徹底同域SSO和同父域SSO。app
1、徹底同域SSO:指的是域名徹底相同的多個應用系統中實現單點登陸。jsp
其實現步驟主要分爲:前期準備工做,編寫統一登陸接口,編寫登陸校驗接口,編寫驗證頁面,實現SSO;測試
編寫統一登陸接口:
登陸頁面的編寫主要是錄入用戶的登陸信息,包括用戶名,密碼,以及登陸頁面的地址。由於存在多個應用系統,因此用戶在統一登陸頁面登錄成功後,系統須要知道用戶想要訪問的是哪一個系統頁面,這個時候就須要記錄用戶第一次訪問的地址,等到用戶登陸成功後,就可直接跳轉該頁面。this
<body> <center> <h1>請登陸</h1> <form action="/sso/doLogin.action" method="POST"> <span>用戶名:</span><input type="text" name="username" /> <span>密碼:</span><input type="text" name="password" /> //暫存須要登陸頁面的url地址 <input type="hidden" name="gotoUrl" value="${gotoUrl}" /> <input type="submit" /> </form> </center> </body>
登陸方法的編寫:須要新建cookie,將用戶的信息存進cookie中,並指定cookie的路徑,由於是徹底同域,因此cookie的地址能夠簡寫爲(「/」)。這裏必需要設置cookie的路徑,若是不設置,那麼cookie的路徑將並不必定在當前域名的頂層,它有可能就在當前的這個路徑下才可見,這樣會致使在當前域的其餘路徑下找不到這個cookie,解決辦法就是把cookie設置到當前域的最頂層域裏面,這樣當前域下的全部應用就會均可見。url
public String doLogin(){ //新建Cookie Cookie cookie = new Cookie("ssocookie","sso"); //設置Cookie路徑 cookie.setPath("/"); HttpServletResponse response = ServletActionContext.getResponse(); response.addCookie(cookie); return "success"; }
登陸校驗接口的編寫:一般SSO登陸接口校驗都會放在登陸攔截器中,當用戶想要訪問某個系統時,登陸攔截器將直接重定向到統一登陸頁面,用戶填寫完登陸信息後,就會進行登陸校驗。spa
//cookie校驗(放在攔截器中進行校驗) public static boolean checkCookie(HttpServletRequst request){ Cookie[] cookies=request.getCookies(); if(cookies!=null){ for(Cookies cookie:cookies){ if(cookie.getName().equals("ssocookie") &&cookie.getValue().equals("sso")){ return true; } } } return false; }
編寫測試主頁code
public String main(){ HttpServletRequst request = ServletActionContext.getRequst(); if(SSOCheck.checkCookie(request)){ //登錄成功,記錄用戶登陸信息...... return "success"; } //登陸失敗,暫存須要訪問的地址,登陸成功後,直接訪問該地址 gotoUrl="/demo1/main.action"; return "login"; }
最後還有struts2的配置文件
<struts> <package name="sso" namespace="/sso" extends="struts-default"> <action name="doLogin" class="com.xm.controllerAction.SSOAction" method="doLogin">
<!-- 用戶登陸成功後,須要進行重定向,從新跳轉到用戶最初訪問的路徑 --> <result name="success" type="redirect">${gotoUrl}</result> </action> </package> <package name="demo1" namespace="/demo1" extends="struts-default"> <action name="main" class="com.xm.controllerAction.Demo1Action" method="main"> <result name="success">/success1.jsp</result> <result name="login">/login.jsp</result> </action> </package> <package name="demo2" namespace="/demo2" extends="struts-default"> <action name="main" class="com.xm.controllerAction.Demo2Action" method="main"> <result name="success">/success2.jsp</result> <result name="login">/login.jsp</result> </action> </package> </struts>
2、同父域SSO:指的是父域名相同的應用系統上實現SSO。
其實現步驟與上述徹底同域SSO相同。
其中檢驗域名:http://check.x.com
測試頁面域名:http://demo1.x.com和http://demo2.x.com
編寫統一登陸接口:
代碼實現與徹底同域名SSO基本一致,不過在設置提交路徑時,由於二級域名不一樣,因此不能寫成相對路徑,須要寫成絕對路徑地址。
<body> <center> <h1>請登陸</h1>
//表單提交地址需寫成絕對路徑,不能寫相對路徑 <form action="http://check.x.com/sso/doLogin.action" method="POST"> <span>用戶名:</span><input type="text" name="username" /> <span>密碼:</span><input type="text" name="password" /> //暫存須要登陸頁面的url地址 <input type="hidden" name="gotoUrl" value="${gotoUrl}" /> <input type="submit" /> </form> </center> </body>
登陸方法:在設置cookie路徑的時候有所變化,同上,爲了使得當前兩個同父域的應用系統均可見這個cookie,那麼咱們須要將這個cookie設置到父域下面,而不該該設置到本域下面,這樣才能夠實現域不一樣,可是父域相同的應用均可以看到的這個cookie。
public String doLogin(){
boolean ok = SSOCheck.checkLogin(userName,passWord);
if(ok){ //新建Cookie Cookie cookie = new Cookie("ssocookie","sso"); //設置Cookie的父域 cookie.setDomain(".x.com"); //設置Cookie路徑 cookie.setPath("/"); HttpServletResponse response = ServletActionContext.getResponse(); response.addCookie(cookie); return "success";
}
}
登陸校驗接口:由於有着不一樣的域,因此咱們應該將登陸所得到cookie傳到專門的校驗域下的校驗方法中進行校驗,不然咱們須要在各自不一樣的登陸頁面實現校驗,這樣顯得代碼十分的冗餘。
private String cookieName;
private String cookieValue;
public String getCookieName() {
return cookieName;
}
public void setCookieName(String cookieName) {
this.cookieName = cookieName;
}
public String getCookieValue() {
return cookieValue;
}
public void setCookieValue(String cookieValue) {
this.cookieValue = cookieValue;
}
//二級域名向二級域名發送請求 public void checkCookie() throws IOException{ boolean ok=SSOCheck.checkCookie(cookieName,cookieValue); String result="0"; if(ok){ result="1"; } HttpServletResponse response = ServletActionContext.getResponse(); response.getWriter().print(result); response.getWriter().close(); }
public static boolean checkCookie(String cookieName,String cookieValue){ if(cookieName.equals("ssocookie")&&cookieValue.equals("sso")){ return true; } return false; }
編寫測試主頁
public String main(){ HttpServletRequst request = ServletActionContext.getRequst();
//獲取cookie Cookie[] cookies=request.getCookies(); if(cookies!=null){ for(Cookie cookie:cookies){ if(cookie.getName().equals("ssocookie")){
//向檢驗服務器中發送cookieName和cookieValue String result = Demo1Tool.doGet("http://check.x.com/so/checkCookie.action", cookie.getName(),cookie.getValue()); if(result.equals("1")){ return "success"; } } } } //暫存須要訪問的地址,登陸成功後,直接訪問該地址 gotoUrl="http://demo1.x.com/demo1/main.action"; return "login"; }
public static String doGet(String url,String cookieName,String cookieValue){ //定義返回值 StringBuffer sb = new StringBuffer(); HttpURLConnection httpURLConnection = null; try{ //校驗方法所在的地址 URL urls = new URL(url+ "?cookieName="+cookieName+"&cookieValue="+cookieValue); //打開鏈接 httpURLConnection = (HttpURLConnection) urls.openConnection(); //設置打開鏈接的方法 httpURLConnection.setRequestMethod("GET"); //開始鏈接 httpURLConnection.connect(); InputStream in = httpURLConnection.getInputStream(); InputStreamReader isr = new InputStreamReader(in); BufferedReader br = new BufferedReader(isr); String temp = null; while((temp = br.readLine())!=null){ sb.append(temp); } br.close(); isr.close(); in.close(); } catch(IOException e){ e.printStackTrace(); } finally{ if(httpURLConnection!=null){ httpURLConnection.disconnect(); } } return sb.toString(); }
struts配置文件
<struts> <package name="sso" namespace="/sso" extends="struts-default"> <action name="doLogin" class="com.xm.controllerAction.SSOAction" method="doLogin"> <result name="success" type="redirect">${gotoUrl}</result> </action> <action name="checkCookie" class="check.x.com.SSOAction" method="checkCookie"> </action> </package> <package name="demo1" namespace="/demo1" extends="struts-default"> <action name="main" class="demo1.x.com.Demo1Action" method="main"> <result name="success">/success1.jsp</result> <result name="login">/login.jsp</result> </action> </package> <package name="demo2" namespace="/demo2" extends="struts-default"> <action name="main" class="demo2.x.com.Demo2Action" method="main"> <result name="success">/success2.jsp</result> <result name="login">/login.jsp</result> </action> </package> </struts>
3、跨域SSO:在域名徹底不一樣的應用程序上實現SSO。
其實現步驟與徹底同域SSO相同。
其中檢驗域名:http://www.x.com
測試頁面域名:http://www.a.com和http://www.b.com
編寫統一登陸接口:
<body> <center> <h1>請登陸</h1> //這裏須要向當前所在的域提交申請,由於若是向校驗域提交申請,那麼在本域中將沒法看到cookie <form action="/${path}/doLogin.action" method="POST"> <span>用戶名:</span><input type="text" name="username" /> <span>密碼:</span><input type="text" name="password" /> //暫存須要登陸頁面的url地址 <input type="hidden" name="gotoUrl" value="${gotoUrl}" /> <input type="submit" /> </form> </center> </body>
登陸方法:
public String doLogin(){ Map<String,String> map = new HashMap<String,String>(); map.put("userName", userName); map.put("password", passWord); String result = Demo1Tool.doGet("http://www.x.com/sso/doLogin.action",map); if(result.equals("1")){ return "success"; } return "login"; }
public void doLogin() throw IOException{ boolean ok=SSOCheck.checkCookie(cookieName,cookieValue); String result = "0"; if(ok){ result = "1"; } HttpServletResponse response = ServletActionContext.getResponse(); response.getWriter().print(result); response.getWriter().close(); }
登陸校驗接口;和同父域SSO的登陸校驗基本一致,沒有什麼變化。
public void checkCookie() throws IOException{ boolean ok=SSOCheck.checkCookie(cookieName,cookieValue); String result="0"; if(ok){ result="1"; } HttpServletResponse response = ServletActionContext.getResponse(); response.getWriter().print(result); response.getWriter().close(); }
編寫測試主頁
public String main(){ HttpServletRequst request = ServletActionContext.getRequst(); Cookie[] cookies=request.getCookies(); if(cookies!=null){ for(Cookie cookie:cookies){ if(cookie.getName().equals("ssocookie")){ Map<String,String> map = new HashMap<String,String>(); map.put("userName", cookie.getName()); map.put("password", cookie.getValue()); String result = Demo1Tool.doGet("http://www.x.com/so/checkCookie.action", cookie.getName(),cookie.getValue()); if(result.equals("1")){ return "success"; } } } } //暫存須要訪問的地址,登陸成功後,直接訪問該地址 path = "demo1"; gotoUrl="http://www.a.com/demo1/main.action"; return "login"; }
public static String doGet(String url,Map<String,String> map){ //定義返回值 StringBuffer sb = new StringBuffer(); HttpURLConnection httpURLConnection = null; try{ StringBuffer t_s = new StringBuffer(url).append("?"); for(Map.Entry<String, String> entry:map.entrySet()){ t_s.append(entry.getKey()).append("=").append(entry.getValue()).append("&"); } url = t_s.substring(0,t_s.length()-1); //校驗方法所在的地址 URL urls = new URL(url); //打開鏈接 httpURLConnection = (HttpURLConnection) urls.openConnection(); //設置打開鏈接的方法 httpURLConnection.setRequestMethod("GET"); //開始鏈接 httpURLConnection.connect(); InputStream in = httpURLConnection.getInputStream(); InputStreamReader isr = new InputStreamReader(in); BufferedReader br = new BufferedReader(isr); String temp = null; while((temp = br.readLine())!=null){ sb.append(temp); } br.close(); isr.close(); in.close(); } catch(IOException e){ e.printStackTrace(); } finally{ if(httpURLConnection!=null){ httpURLConnection.disconnect(); } } return sb.toString(); }
至此,準備工做作完,接下來是跨域SSO實現中最重要的部分,也就是cookie的設置,在以前徹底同域和同父域的狀況下,爲了實現SSO,咱們在進行doLogin時就設置了cookie,由於域名相同,因此十分簡單,可是在跨域SSO中,由於不一樣域之間的cookie是不可見的,因此咱們不可能只設置一個cookie,而後令全部的域名下的應用程序皆可見,因此咱們應該在每一個域下面都有着爲本域設置cookie的方法,而不該該直接將設置cookie交給校驗域。
//爲本域設置cookie的方法 public void addCookie(){ Cookie cookie = new Cookie("ssocookie","sso"); cookie.setPath("/"); HttpServletResponse response = ServletActionContext.getResponse(); response.addCookie(cookie); }
還須要在配置文件中進行配置:
<action name="addCookie" class="www.a.com.Demo1Action" method="addCookie"></action>
寫無缺方法,則須要進行調用,所以咱們須要找一個可讓兩者進行交會的地方,在這裏我選擇了登陸成功的瞬間,經過隱藏的Iframe讓兩者進行交會。
public String doLogin(){ Map<String,String> map = new HashMap<String,String>(); map.put("userName", userName); map.put("password", passWord); String result = Demo1Tool.doGet("http://www.x.com/sso/doLogin.action",map); if(result.equals("1")){ return "success"; } List hidderUrl = new ArrayList<String>(); hidderUrl.add("http://www.a.com/demo1/addCookie.action"); hidderUrl.add("http://www.b.com/demo2/addCookie.action"); return "login"; }
<c:forEach var="url" item="${hiddenUrl}">
<iframe src="${url}" width="0px" heigth="0px" style="display:none"></iframe>
</c:forEach>