java單點登錄(SSO)的實現(xiàn)
單點登錄(SSO):SSO是指在多個應(yīng)用系統(tǒng)中個,用戶只需要登陸一次就可以訪問所有相互信任的應(yīng)用系統(tǒng)。它包括可以將這次主要的登錄映射到其他應(yīng)用中用于同一用戶的登陸的機制。
SSO的實現(xiàn)過程:
通過上述圖形,我們可以看到SSO的大體實現(xiàn)步驟主要分為兩大步:存儲登錄信息,查驗登錄信息。
對于SSO,我們也可以將之分為兩大不同的類型:同域SSO和跨域SSO;其中同域SSO又可以分為完全同域SSO和同父域SSO。
一、完全同域SSO:指的是域名完全相同的多個應(yīng)用系統(tǒng)中實現(xiàn)單點登錄。
其實現(xiàn)步驟主要分為:前期準(zhǔn)備工作,編寫統(tǒng)一登錄接口,編寫登錄校驗接口,編寫驗證頁面,實現(xiàn)SSO;
編寫統(tǒng)一登錄接口:
登錄頁面的編寫主要是錄入用戶的登錄信息,包括用戶名,密碼,以及登錄頁面的地址。因為存在多個應(yīng)用系統(tǒng),所以用戶在統(tǒng)一登錄頁面登陸成功后,系統(tǒng)需要知道用戶想要訪問的是哪個系統(tǒng)頁面,這個時候就需要記錄用戶第一次訪問的地址,等到用戶登錄成功后,就可直接跳轉(zhuǎn)該頁面。
<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的地址可以簡寫為(“/”)。這里必須要設(shè)置cookie的路徑,如果不設(shè)置,那么cookie的路徑將并不一定在當(dāng)前域名的頂層,它有可能就在當(dāng)前的這個路徑下才可見,這樣會導(dǎo)致在當(dāng)前域的其他路徑下找不到這個cookie,解決辦法就是把cookie設(shè)置到當(dāng)前域的最頂層域里面,這樣當(dāng)前域下的所有應(yīng)用就會都可見。
public String doLogin(){ //新建Cookie Cookie cookie = new Cookie("ssocookie","sso"); //設(shè)置Cookie路徑 cookie.setPath("/"); HttpServletResponse response = ServletActionContext.getResponse(); response.addCookie(cookie); return "success"; }
登錄校驗接口的編寫:通常SSO登錄接口校驗都會放在登錄攔截器中,當(dāng)用戶想要訪問某個系統(tǒng)時,登錄攔截器將直接重定向到統(tǒng)一登錄頁面,用戶填寫完登錄信息后,就會進行登錄校驗。
//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; }
編寫測試主頁
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"> <!-- 用戶登錄成功后,需要進行重定向,重新跳轉(zhuǎn)到用戶最初訪問的路徑 --> <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>
二、同父域SSO:指的是父域名相同的應(yīng)用系統(tǒng)上實現(xiàn)SSO。
其實現(xiàn)步驟與上述完全同域SSO相同。
其中檢驗域名:http://check.x.com
測試頁面域名:http://demo1.x.com和http://demo2.x.com
編寫統(tǒng)一登錄接口:
代碼實現(xiàn)與完全同域名SSO基本一致,不過在設(shè)置提交路徑時,因為二級域名不同,所以不能寫成相對路徑,需要寫成絕對路徑地址。
<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>
登錄方法:在設(shè)置cookie路徑的時候有所變化,同上,為了使得當(dāng)前兩個同父域的應(yīng)用系統(tǒng)都可見這個cookie,那么我們需要將這個cookie設(shè)置到父域下面,而不應(yīng)該設(shè)置到本域下面,這樣才可以實現(xiàn)域不同,但是父域相同的應(yīng)用都可以看到的這個cookie。
public String doLogin(){ boolean ok = SSOCheck.checkLogin(userName,passWord); if(ok){ //新建Cookie Cookie cookie = new Cookie("ssocookie","sso"); //設(shè)置Cookie的父域 cookie.setDomain(".x.com"); //設(shè)置Cookie路徑 cookie.setPath("/"); HttpServletResponse response = ServletActionContext.getResponse(); response.addCookie(cookie); return "success"; } }
登錄校驗接口:因為有著不同的域,所以我們應(yīng)該將登錄所獲得cookie傳到專門的校驗域下的校驗方法中進行校驗,否則我們需要在各自不同的登錄頁面實現(xiàn)校驗,這樣顯得代碼十分的冗余。
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; } //二級域名向二級域名發(fā)送請求 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")){ //向檢驗服務(wù)器中發(fā)送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(); //設(shè)置打開連接的方法 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>
三、跨域SSO:在域名完全不同的應(yīng)用程序上實現(xiàn)SSO。
其實現(xiàn)步驟與完全同域SSO相同。
其中檢驗域名:http://www.x.com
測試頁面域名:http://www.a.com和http://www.b.com
編寫統(tǒng)一登錄接口:
<body> <center> <h1>請登錄</h1> //這里需要向當(dāng)前所在的域提交申請,因為如果向校驗域提交申請,那么在本域中將無法看到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(); //設(shè)置打開連接的方法 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(); }
至此,準(zhǔn)備工作做完,接下來是跨域SSO實現(xiàn)中最重要的部分,也就是cookie的設(shè)置,在之前完全同域和同父域的情況下,為了實現(xiàn)SSO,我們在進行doLogin時就設(shè)置了cookie,因為域名相同,所以十分簡單,但是在跨域SSO中,因為不同域之間的cookie是不可見的,所以我們不可能只設(shè)置一個cookie,然后令所有的域名下的應(yīng)用程序皆可見,所以我們應(yīng)該在每個域下面都有著為本域設(shè)置cookie的方法,而不應(yīng)該直接將設(shè)置cookie交給校驗域。
//為本域設(shè)置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>
寫完好方法,則需要進行調(diào)用,因此我們需要找一個可以讓二者進行交會的地方,在這里我選擇了登錄成功的瞬間,通過隱藏的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>
到此這篇關(guān)于java單點登錄(SSO)的實現(xiàn)的文章就介紹到這了,更多相關(guān)java單點登錄內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
詳解java中List中set方法和add方法的區(qū)別
本文主要介紹了詳解java中List中set方法和add方法的區(qū)別,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2022-08-082020新版idea創(chuàng)建項目沒有javaEE 沒有Web選項的完美解決方法
這篇文章主要介紹了2020新版idea創(chuàng)建項目沒有javaEE 沒有Web選項的完美解決方法,本文給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-09-09springboot?html調(diào)用js無效400問題及解決
這篇文章主要介紹了springboot?html調(diào)用js無效400的問題及解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-03-03淺談在Java中使用Callable、Future進行并行編程
這篇文章主要介紹了淺談在Java中使用Callable、Future進行并行編程,具有一定借鑒價值,需要的朋友可以參考下。2017-12-12java并發(fā)數(shù)據(jù)包Exchanger線程間的數(shù)據(jù)交換器
這篇文章主要為大家介紹了java并發(fā)數(shù)據(jù)包使用數(shù)據(jù)交換器Exchanger來進行線程之間的數(shù)據(jù)交換。有需要的朋友可以借鑒參考下,希望能夠有所幫助2022-03-03詳解Java8中接口的默認(rèn)方法和靜態(tài)方法
Java 8是Java語言的一個重要版本,其中引入了許多新特性和改進,其中一個值得關(guān)注的特性是接口的默認(rèn)方法和靜態(tài)方法,本文就來和大家簡單講講吧2023-05-05