單點登錄(SSO——Single Sign On)對于我們來說已經不陌生了。對于大型系統來說使用單點登錄可以減少用戶很多的麻煩。就拿百度來說吧,百度下面有很多的子系統——百度經驗、百度知道、百度文庫等等,如果我們使用這些系統的時候,每一個系統都需要我們輸入用戶名和密碼登錄一次的話,我相信用戶體驗肯定會直線下降。當然,對于個人博客這類系統來說根本就用不上單點登錄了。
假如,我們的系統很龐大,但是就是這一個系統,并沒有什么子系統。這時我們也不需要單點登錄。這里不涉及多臺主機負載均衡session共享的問題。
在同一個域名下的不同站點是如何進行驗證的
如果兩個站點可以共享相同的驗證Cookie,這將很容易實現使用同一個用戶登錄多個站點。按照HTTP協議規定,兩個站點是可以共享Cookie的。前提是這兩個站點是在同一個域名下面(或者是二級域名也可)。這種情況是屬于同域下的Cookie。瀏覽器會將Cookie以及該Cookie所屬的域存在本地。當你對該域下的任何子站點進行訪問的時候,瀏覽器都會將這些Cookie發送給站點系統。
假設我們有兩個站點
www.onmpw.com/site1
www.onmpw.com/site2
這兩個站點共享同一個主機地址,并且二者在同一域名下。加入你剛剛登錄了www.onmpw.com/site1,你的瀏覽器會有一個來自www.onmpw.com/site1的身份鑒證的cookie。當你點擊site1下的任何的子頁面的時候,這些cookie都會發送給site1。這是很容易理解的。同樣的,當你請求www.onmpw.com/site2的時候,對于site2下面的任何頁面這些cookie也同樣會隨著請求發送過去。為什么是這樣,因為在瀏覽器端存儲的cookie的域是www.onmpw.com。site1和site2兩個站點是同屬于該域的。所以對于該域下的cookie,兩個站點都可以得到。只需要按照正常的驗證方式進行驗證即可。因為二者的sessionId是相同的,只要它們的session信息是保存在同一個地方即可。
同一個域但是不同的子域如何進行單點登錄
假如我們的站點是按照下面的域名進行部署的
sub1.onmpw.com
sub2.onmpw.com
這兩個站點共享同一域onmpw.com。默認情況下,瀏覽器會發送cookie所屬的域對應的主機。也就是說,來自于sub1.onmpw.com的cookie默認所屬的域是.sub1.onmpw.com。因此,sub2.onmpw.com不會得到任何的屬于sub1.onmpw.com的cookie信息。因為它們是在不同的主機上面,并且二者的子域也是不同的。
這時可以設置二者的cookie信息在同一個域下。
1.登錄sub1.onmpw.com系統
2.登錄成功以后,設置cookie信息。這里需要注意,我們可以將用戶名和密碼存到cookie中,但是在設置的時候必須將這cookie的所屬域設置為頂級域 .onmpw.com。這里可以使用setcookie函數,該函數的第四個參數是用來設置cookie所述域的。
cookie.setDomain(".onmpw.com");
3.訪問sub2.onmpw.com系統,瀏覽器會將cookie中的信息username和password附帶在請求中一塊兒發送到sub2.onmpw.com系統。這時該系統會先檢查session是否登錄,如果沒有登錄則驗證cookie中的username和password從而實現自動登錄。
4.sub2.onmpw.com 登錄成功以后再寫session信息。以后的驗證就用自己的session信息驗證就可以了。
當然,先登錄sub2.onmpw.com的方式也是相同的。經過上面的步驟就可以實現不同二級域名的單點登錄了。
但是,這里存在一個問題就是sub1系統退出以后,除了可以清除自身的session信息和所屬域為.onmpw.com的cookie的信息。它并不能清除sub2系統的session信息。那sub2仍然是登錄狀態。也就是說,這種方式雖說可以實現單點登錄,但是不能實現同時退出。原因是,我們只是把用戶名和密碼的cookie通過setDomain設置成共享cookie。但是二者的sessionId是不同的,而且這個sessionId在瀏覽器中也是以cookie的形式存儲的,不過它所屬的域并不是.onmpw.com。也就是說二者的sessionId是不同的。
解決方案:
把第一次登錄生成的JSESSIONID,通過setDomain放到一個共享的的自定義的cookie中。之后訪問二級域名的時候,將自定義cookie中的的值取出來,然后在放到JSESSIONID的cookie的值中。參考
Cookie c = new Cookie("JSESSIONID", session.getId());
c.setDomain("abc.com");
resp.addCookie(c);
要點:不同的子域的Cookie可以共享。Session跨子域需要把JESSIONID寫進共享Cookie(雖然JSEESIONID就是本身Cookie,但由于tomcat創建的,他的domain屬性是跟你當前站的域名是嚴格保持一致的)。