單點登錄:SSO(Single Sign On)
什么是單點登錄:大白話就是多個網站共享一個用戶名和密碼的技術,對於普通用戶來說,只需要登錄其中任意一個網站,登錄其他網站的時候就能夠自動登陸,不需要再輸入
用戶名和密碼了。
單點登錄類型:
1.具有相同一級域名的多個網站,以新浪為例,新浪首頁實際上是一個導航頁面,它提供了很多很多的旗下網站地址,比如
(1)新浪新聞: http://news.sina.com.cn/
(2)新浪科技:http://tech.sina.com.cn/
(3)新浪博客:http://blog.sina.com.cn/
等等,它們都有相同的一級域名.sina.com.cn
使用Cookie將登陸信息寫到本地,域名指定為.sina.com.cn,那么在訪問新浪博客http://blog.sina.com.cn/的時候就會帶有登陸信息,使用過濾器即可實現自動登陸。
2.沒有相同域名的多個網站,比如淘寶和天貓
(1)淘寶:https://www.taobao.com/
(2)天貓:https://www.tmall.com/
雖然是兩個不同的網站,而且沒有相同的域名,但是在登陸淘寶之后,再訪問天貓頁面就會自動登陸,這種實現的技術就不僅僅是1.中具有相同一級域名的時候那么簡單了。在java中,使用CAS技術解決這種情況比較常見。
一、使用過濾器和Cookie技術具有相同一級域名的多個網站實現單點登錄
項目練習地址:https://github.com/kdyzm/day68_SSO
1.在Tomcat中配置多個站點的方法
(1)首先修改C:\Windows\System32\drivers\etc\hosts配置文件
添加的內容如下:
#有相同的一級域名的單點登錄測試 127.0.0.1 www.bbs.kdyzm.com 127.0.0.1 www.news.kdyzm.com
(2)修改%Tomcat_Home%/conf/server.xml配置文件,在<Host></Host>標簽后面添加下面的配置:
<!--有相同一級域名的單點登錄測試-->
</Host>
<Host name="www.news.kdyzm.com" appBase="news">
</Host>
<Host name="www.bbs.kdyzm.com" appBase="bbs">
</Host>
(3)上面的appBase指的是%Tomcat_Home%路徑下的文件夾,所以創建兩個同名的文件夾(news、bbs)分別對應appBase中的內容,這兩個文件夾和webapps文件夾同級。

這樣就有了三個站點,分別是bbs、news、webapps
2.新建Jsp文件,使用jsp語法進行判斷是否已經登陸,針對是否已經登錄顯示不同的內容:已經登陸的顯示歡迎話語;沒有登陸過的顯示登錄表單。
/index.jsp內容如下:
1 <body>
2 <c:choose>
3 <c:when test="${not empty sessionScope.user}">
4 歡迎你,${sessionScope.user.userName}
5 </c:when>
6 <c:otherwise>
7 <form method="post"
8 action="${pageContext.servletContext.contextPath}/loginServlet">
9 <table>
10 <tr>
11 <td>用戶名</td>
12 <td><input type="text" name="userName"></td>
13 </tr>
14 <tr>
15 <td>密碼</td>
16 <td><input type="text" name="password"></td>
17 </tr>
18 <tr>
19 <td><input type="submit" value="登陸"></td>
20 <td><input type="reset" value="重置"></td>
21 </tr>
22 </table>
23 </form>
24 </c:otherwise>
25 </c:choose>
26 </body>
3.書寫Servlet類
/loginServlet 中的內容如下
1 package com.kdyzm.servlet;
2
3 import java.io.IOException;
4
5 import javax.servlet.ServletException;
6 import javax.servlet.http.Cookie;
7 import javax.servlet.http.HttpServlet;
8 import javax.servlet.http.HttpServletRequest;
9 import javax.servlet.http.HttpServletResponse;
10
11 import com.kdyzm.domain.User;
12
13 public class LoginServlet extends HttpServlet {
14 private static final long serialVersionUID = -7472135565931819576L;
15 @Override
16 protected void doGet(HttpServletRequest req, HttpServletResponse resp)
17 throws ServletException, IOException {
18 this.doPost(req, resp);
19 }
20 @Override
21 protected void doPost(HttpServletRequest request, HttpServletResponse response)
22 throws ServletException, IOException {
23 request.setCharacterEncoding("utf-8");
24 response.setContentType("text/html;charset=utf-8");
25
26 String userName=request.getParameter("userName");
27 String password=request.getParameter("password");
28 if(userName.equals(password)){
29 User user=new User();
30 user.setUserName(userName);
31 user.setPassword(password);
32 //保存到Session
33 request.getSession().setAttribute("user", user);
34 //保存到Cookie
35 Cookie cookie=new Cookie("userName",user.getUserName());
36 cookie.setMaxAge(3600); //設置生命周期為一個小時
37 cookie.setDomain(".kdyzm.com"); //設置域名
38 cookie.setPath("/"); //設置路徑為根路徑
39 response.addCookie(cookie);
40 }
41 response.sendRedirect(request.getContextPath()+"/index.jsp");
42 }
43 }
到此為止已經實現了登陸的功能,並且將內容寫入到了Cookie和Session中。
下一步實現自動登錄功能,這時候就需要一個過濾器進行判斷請求的信息中是否帶有登錄信息。
4.AutoLoginFilter過濾器的書寫如下:
1 package com.kdyzm.filter;
2
3 import java.io.IOException;
4
5 import javax.servlet.Filter;
6 import javax.servlet.FilterChain;
7 import javax.servlet.FilterConfig;
8 import javax.servlet.ServletException;
9 import javax.servlet.ServletRequest;
10 import javax.servlet.ServletResponse;
11 import javax.servlet.http.Cookie;
12 import javax.servlet.http.HttpServletRequest;
13 import javax.servlet.http.HttpServletResponse;
14 import javax.servlet.http.HttpSession;
15
16 import com.kdyzm.domain.User;
17
18 /**
19 * 不做登錄驗證,只做自動登錄
20 * @author kdyzm
21 *
22 */
23 public class AutoLoginFilter implements Filter{
24
25 @Override
26 public void init(FilterConfig filterConfig) throws ServletException {
27 System.out.println("過濾器初始化!");
28 }
29
30 @Override
31 public void doFilter(ServletRequest req, ServletResponse resp,
32 FilterChain chain) throws IOException, ServletException {
33 HttpServletRequest request=(HttpServletRequest) req;
34 HttpServletResponse response=(HttpServletResponse) resp;
35 HttpSession session=request.getSession();
36 System.out.println("被攔截器過濾!");
37 //先判斷是否已經登陸再做其他的判斷比較高效
38 if(session.getAttribute("user")==null){
39 System.out.println("還沒有登陸!");
40 Cookie [] cookies=request.getCookies();
41 if(cookies!=null){
42 for(Cookie cookie:cookies){
43 String name=cookie.getName();
44 if(name.equals("userName")){
45 System.out.println("還沒有登錄,但是存在登錄信息!");
46 User user=new User();
47 String userName=cookie.getValue();
48 String password=userName;
49 user.setUserName(userName);
50 user.setPassword(password);
51 session.setAttribute("user", user);
52 }
53 System.out.println(cookie.getName()+":"+cookie.getValue());
54 }
55 }
56 }
57 chain.doFilter(request, response);
58 }
59
60 @Override
61 public void destroy() {
62 System.out.println("過濾器被銷毀!");
63 }
64
65 }
到此,准備工作已經完成,下一步進行部署工作。
5.在新建立的兩個站點下分別新建ROOT文件夾,並且將MyEclipse項目中Webroot下的所有文件拷貝過去
為什么要新建ROOT文件夾?tomcat服務器默認訪問站點下的ROOT文件夾中的內容,使用ROOT文件夾訪問的時候就不需要加上項目名稱了。
6.分別修改兩個站點下的index.jsp頁面,目的是在測試的時候方便區分。
比如在bbs站點下的index.jsp頁面中的修改內容:
<c:choose>
<c:when test="${not empty sessionScope.user}">
歡迎你,${sessionScope.user.userName}<br/>
這里是論壇界面!
</c:when>
在news站點下的修改內容
<c:choose>
<c:when test="${not empty sessionScope.user}">
歡迎你,${sessionScope.user.userName}<br/>
這里是新聞界面!
</c:when>
7.啟動tomcat,進行測試,這里使用火狐瀏覽器進行測試
首先,訪問http://localhost/day68_SSO/,輸入英文用戶名和密碼,兩者保證是相同的字符串。以xiaozhang為例:


查看Cookie信息,發現並沒有期望中的.kdyzm.com的cookie信息,這是因為我們訪問的是localhost:

接着訪問www.bbs.kdyzm.com,出現的頁面和之前一模一樣,這次我們輸入bbs為登錄名和密碼:


查看cookie信息,發現多了兩種cookie信息,這時候,.kdyzm.com域中的cookie已經寫成功了,而且其生命周期為一個小時、域為.kdyzm.com、名稱為userName、內容為bbs這些內容都和程序設計的預期完全相同;同時bbs.kdyzm.com中也有了新的cookie信息,這個信息只是自動分配的JSESSIONID信息:

最后登錄www.news.kdyzm.com,發現已經自動登陸了:

查看Cookie信息,發現多了news.kdyzm.com的站點信息的Cookie,但是並不是登陸信息的Cookie,只是JSESSIONID而已,它發送的Cookie信息是從kdyzm.com域名中獲取到的。

二、沒有相同的一級域名的實現多個站點的單點登錄的方法:CAS
1.使用CAS框架實現多個站點如果沒有相同的一級域名的單點登錄測試
(1)CAS:Central Authentication Service,翻譯過來就是中心認證服務,顧名思義,該框架的功能就是“認證”
使用CAS框架實現多個不同站點的單點登錄的原理的關鍵就是使用一個認證服務器集中處理登陸的事宜。
流程圖如下圖所示:

(2)使用CAS完成不同站點的單點登錄的demo下載地址:
https://github.com/kdyzm/day68_SSO_CAS_Demo
2.運行demo測試的步驟
1).首先需要修改hosts配置文件:添加上一下的兩行信息:
127.0.0.1 www.bbs.com 127.0.0.1 www.news.com
2).修改hosts文件,添加一下的三行信息
<Host name="www.server.com" appBase="server">
</Host>
<Host name="www.news.com" appBase="news1">
</Host>
<Host name="www.bbs.com" appBase="bbs1">
</Host>
3).將news1文件夾和bbs1文件夾分別拷貝到%Tomcat_Home%文件夾中,分別作為兩個獨立的站點;將server文件夾拷貝到%Tomcat_Home%文件夾中,作為認證服務器站點。
4).啟動tomcat服務器。首先訪問www.server.com,如果沒有任何問題的話,才能進行下一步。出現的界面如下:
5).隨便輸入一個用戶名,但是必須確保密碼和用戶名相同,這是服務端默認的一個認證規則。如果成功了,則會顯示如下界面:

清空瀏覽器cookies信息,然后進行下一步。
6).訪問www.bbs.com,單擊下圖中的“to to protected area”超鏈接,在顯示出來的頁面中輸如bbs作為用戶名,使用bbs作為密碼登陸


注意上圖中的地址欄部分,已經不是www.bbs.com站點了,而是www.server.com。
登陸成功:

7).訪問www.news.com,直接單擊"go to protected area",發現已經自動登陸了。

3.演示到此結束,進行該測試的時候必須使用MyEclipse啟動tomcat服務器;如果只是單獨使用tomcat則很有可能會無法訪問www.server.com。
4.具體使用方法比較復雜,但是這種技術在真正的開發中可能用不着。暫時存檔。

