本文來自:高爽|Coder,原文地址:http://blog.csdn.net/ghsau/article/details/20466351,轉載請註明。
最近在公司作了Web端單點登陸(SSO)功能,基於Cookie實現,作完以後感受有必要總結一下,本文着重講解Cookie,下文會說明單點登陸的實現方案。css
Cookie簡介html
衆所周知,Web協議(也就是HTTP)是一個無狀態的協議(HTTP1.0)。一個Web應用由不少個Web頁面組成,每一個頁面都有惟一的URL來定義。用戶在瀏覽器的地址欄輸入頁面的URL,瀏覽器就會向Web Server去發送請求。以下圖,瀏覽器向Web服務器發送了兩個請求,申請了兩個頁面。這兩個頁面的請求是分別使用了兩個單獨的HTTP鏈接。所謂無狀態的協議也就是表如今這裏,瀏覽器和Web服務器會在第一個請求完成之後關閉鏈接通道,在第二個請求的時候從新創建鏈接。Web服務器並不區分哪一個請求來自哪一個客戶端,對全部的請求都一視同仁,都是單獨的鏈接。這樣的方式大大區別於傳統的(Client/Server)C/S結構,在那樣的應用中,客戶端和服務器端會創建一個長時間的專用的鏈接通道。正是由於有了無狀態的特性,每一個鏈接資源可以很快被其餘客戶端所重用,一臺Web服務器纔可以同時服務於成千上萬的客戶端。
可是咱們一般的應用是有狀態的。先不用提不一樣應用之間的SSO,在同一個應用中也須要保存用戶的登陸身份信息。例如用戶在訪問頁面1的時候進行了登陸,可是剛纔也提到,客戶端的每一個請求都是單獨的鏈接,當客戶再次訪問頁面2的時候,如何才能告訴Web服務器,客戶剛纔已經登陸過了呢?瀏覽器和服務器之間有約定:經過使用cookie技術來維護應用的狀態。Cookie是能夠被Web服務器設置的字符串,而且能夠保存在瀏覽器中。以下圖所示,當瀏覽器訪問了頁面1時,web服務器設置了一個cookie,並將這個cookie和頁面1一塊兒返回給瀏覽器,瀏覽器接到cookie以後,就會保存起來,在它訪問頁面2的時候會把這個cookie也帶上,Web服務器接到請求時也能讀出cookie的值,根據cookie值的內容就能夠判斷和恢復一些用戶的信息狀態。
java
Cookie組成web
cookie是由名稱、內容、做用路徑、做用域、協議、生存週期組成,另外還有個HttpOnly屬性,HttpOnly屬性很重要,若是您在cookie中設置了HttpOnly屬性,那麼經過js腳本(document.cookie)將沒法讀取到cookie信息,這樣能必定程度上的防止XSS攻擊,關於XSS能夠看我以前的文章--XSS攻擊及防護。Tomcat服務器設置的JSESSIONID就是HttpOnly的。
JavaEE中對cookie作了封裝,對應的是下面這個類:面試
java.lang.Object
|
+--javax.servlet.http.Cookie
該類能夠設置cookie的名稱、內容、做用路徑、做用域、協議、生存週期,but不能設置HttpOnly屬性,不知道這麼作是出於什麼考慮,若是非要設置HttpOnly的cookie,咱們能夠經過響應頭來處理:
- response.setHeader("Set-Cookie", "cookiename=value;Path=/;Domain=domainvalue;Max-Age=seconds;HttpOnly");
response.setHeader("Set-Cookie", "cookiename=value;Path=/;Domain=domainvalue;Max-Age=seconds;HttpOnly");
Cookie做用域
測試Cookie的做用域須要弄幾個域名,修改C:\Windows\System32\drivers\etc\hosts文件,將本機ip映射出四個域名,以下:
- 127.0.0.1 web1.ghsau.com
- 127.0.0.1 web2.ghsau.com
-
- 127.0.0.1 web1.com
- 127.0.0.1 web2.com
127.0.0.1 web1.ghsau.com
127.0.0.1 web2.ghsau.com
127.0.0.1 web1.com
127.0.0.1 web2.com
前兩個是2級域名(ghsau.com)相同,3級域名(web一、web2)不一樣,後兩個是2級域名不一樣。而後咱們再寫兩個jsp,一個用於設置Cookie,另外一個用於顯示Cookie。
SetCookie.jsp:
- <%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8"%>
- <%
- Cookie cookie = new Cookie("test_key", "test_value");
- cookie.setPath("/");
- // cookie.setDomain(".ghsau.com");
- response.addCookie(cookie);
- %>
<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8"%>
<%
Cookie cookie = new Cookie("test_key", "test_value");
cookie.setPath("/");
// cookie.setDomain(".ghsau.com");
response.addCookie(cookie);
%>
ShowCookie.jsp:
- <%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8"%>
- <%
- // 輸出cookies,過濾掉JSESSIONID
- Cookie[] cookies = request.getCookies();
- if(cookies != null)
- for(Cookie cookie : cookies) {
- if(cookie.getName().equals("JSESSIONID")) continue;
- out.println(cookie.getName() + "-" + cookie.getValue());
- }
- %>
<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8"%>
<%
// 輸出cookies,過濾掉JSESSIONID
Cookie[] cookies = request.getCookies();
if(cookies != null)
for(Cookie cookie : cookies) {
if(cookie.getName().equals("JSESSIONID")) continue;
out.println(cookie.getName() + "-" + cookie.getValue());
}
%>
將這兩個jsp放到應用後,部署到服務器中,啓動服務器,咱們就能夠經過域名來訪問了。
測試一,首先訪問http://
web1.ghsau.com:8080/WebSSOAuth/SetCookie.jsp,設置cookie後,再訪問http://
web1.ghsau.com:8080/WebSSOAuth/ShowCookie.jsp,頁面輸出test_key=test_value,這時咱們訪問http://
web2.ghsau.com:8080/WebSSOAuth/ShowCookie.jsp,發現頁面什麼都沒有輸出,這時咱們得出結論,
cookie默認狀況下做用域爲當前域名。
測試二,將SetCookie.jsp第五行註釋打開,按照上面的順序依次訪問,咱們發現http://
web2.ghsau.com:8080/WebSSOAuth/ShowCookie.jsp中輸出了http://
web1.ghsau.com:8080/WebSSOAuth/SetCookie.jsp中設置的cookie,這時咱們得出結論,
cookie做用域爲父級域名時,全部子級域名均可以獲得該cookie,這也是實現跨子域SSO的關鍵。這時有些朋友可能會想到那我把cookie做用域設置到頂級域名(.com、.net)上,是否是用該頂級域名的網站就都能獲取該cookie了?這樣設置的cookie,瀏覽器是不存儲的,無效的cookie。
測試三,修改SetCookie.jsp第五行代碼爲cookie.setDomain(".web2.com"),首先訪問http://web1.com:8080/WebSSOAuth/SetCookie.jsp,設置cookie後,這時咱們訪問http://web2.com:8080/WebSSOAuth/ShowCookie.jsp,發現頁面什麼都沒有輸出,這時咱們得出結論,cookie不能跨二級域名設置。api
Cookie安全性瀏覽器
cookie中的數據一般會包含用戶的隱私數據,首先要保證數據的保密性,其次要保證數據不能被僞造或者篡改,基於這兩點,咱們一般須要對cookie內容進行加密,加密方式通常使用對稱加密(單密鑰,如DES)或非對稱加密(一對密鑰,如RSA),密鑰須要保存在服務器端一個安全的地方,這樣,別人不知道密鑰時,沒法對數據進行解密,也沒法僞造或篡改數據。另外,像上文提到的,重要的cookie數據須要設置成HttpOnly的,避免跨站腳本獲取你的cookie,保證了cookie在瀏覽器端的安全性。還有咱們能夠設置cookie只做用於安全的協議(https),JavaEE中能夠經過Cookie類的setSecure(boolean flag)來設置,設置後,該cookie只會在https下發送,而不會再http下發送,保證了cookie在服務器端的安全性,服務器https的設置能夠參照該文章。
本文來自:高爽|Coder,原文地址:http://blog.csdn.net/ghsau/article/details/20466351,轉載請註明。
安全