此文知識來自于:《深入分析Java_Web技術(shù)》第十章
現(xiàn)代session與cookie的應(yīng)用
本章概要:
當我們的一個應(yīng)用系統(tǒng)有幾百臺服務(wù)器時项郊,如何解決Session在多臺服務(wù)器之間共享的問題枯途?它們還有一些安全問題,如Cookie被盜、Cookie偽造等問題應(yīng)如何避免仅乓?Session與Cookie的作用都是為了保持訪問用戶與后端服務(wù)器的交互狀態(tài)收毫。例如,使用Cookie來傳遞信息時风纠,隨著Cookie個數(shù)的增多和訪問量的增加,它占用的網(wǎng)絡(luò)帶寬也很大牢贸,試想假如Cookie占用200個字節(jié)竹观,如果一天的PV有幾億,那么它要占用多少帶寬潜索?所以有大訪問量時希望用Session臭增,但是Session的致命弱點是不容易在多臺服務(wù)器之間共享,這也限制了Session的使用竹习。
1. 理解Cookie
Cookie的作用通俗地說就是當一個用戶通過HTTP訪問一個服務(wù)器時誊抛,這個服務(wù)器會將一些Key/Value鍵值對返回給客戶端瀏覽器,并給這些數(shù)據(jù)加上一些限制條件整陌,在條件符合時這個用戶下次訪問這個服務(wù)器時拗窃,數(shù)據(jù)又被完整地帶回給服務(wù)器。
當初W3C設(shè)計Cookie時實際考慮的是為了記錄用戶在一段時間內(nèi)訪問Web應(yīng)用的行為路徑泌辫。由于HTTP是一種無狀態(tài)協(xié)議随夸,當用戶的一次訪問請求結(jié)束后,后端服務(wù)器就無法知道下一次來訪問的還是不是上次訪問的用戶震放。例如宾毒,在一個很短的時間內(nèi),如果與用戶相關(guān)的數(shù)據(jù)被頻繁訪問殿遂,可以針對這個數(shù)據(jù)做緩存伍俘,這樣可以大大提高數(shù)據(jù)的訪問性能邪锌。Cookie的作用正是如此,由于是同一個客戶端發(fā)出的請求癌瘾,每次發(fā)出的請求都會帶有第一次訪問時觅丰,服務(wù)端設(shè)置的信息,這樣服務(wù)端就可以根據(jù)Cookie值來劃分訪問的用戶了妨退。
1.1 Cookie屬性項
當前Cookie有兩個版本:Version0和Version1妇萄,它們有兩種設(shè)置響應(yīng)頭的標識,分別是“Set-Cookie”和“Set-Cookie2”咬荷。它們屬性項有些不同冠句。
Version0屬性項:
屬性項 | 屬性項介紹 |
---|---|
NAME=VALUE | 設(shè)置要保存的Key/Value,注意這里的NAME不能和其它屬性項的名字一樣 |
Expires | 過期時間 |
Domain | 生成該Cookie的域名 |
Path | 該Cookie是在當前哪個路徑下生成的 |
Secure | 如果設(shè)置了這個屬性幸乒,那么只會在SSH連接時才會回傳該Cookie |
Expires | 過期時間 |
在Java Web的Servlet規(guī)范并不支持Set-Cookie2響應(yīng)頭懦底,在實際應(yīng)用中Set-Cookie2的一些屬性項卻可以設(shè)置在Set-Cookie中。博主在查看Cookie源碼罕扎,發(fā)現(xiàn)是支持的:

另外也可以從源碼可以得知聚唐,一般所說的Cookie鍵值對,都是值NAME和VALUE屬性腔召,其實Cookie還有其他的屬性杆查,通過Get/Set方法進行獲取和設(shè)置。
另外下面是博主在使用Chrome瀏覽器查看的Cookie:

1.2 Cookie如何工作
當我們用如下方式創(chuàng)建Cookie時:
String getCookie(Cookie[] cookies, String key) {
if (cookies != null) {
for (Cookie cookie : cookies) {
if (cookie.getName().equals(key)) {
return cookie.getValue();
}
}
}
return null;
}
@Override
public void doGet(HttpServletRequest req, HttpServletResponse res) {
Cookie[] cookies = req.getCookies();
String userName = getCookie(cookies, "userName");
String userAge = getCookie(cookies, "userAge");
if (userName == null) {
res.addCookie(new Cookie("userName", "liwenguang"));
}
if (userAge == null) {
res.addCookie(new Cookie("userAge", "22"));
}
res.getHeaders("Set-Cookie");
}
以下幾點需要注意:
- 所創(chuàng)建Cookie的NAME不能和Set-Cookie或者Set-Cookie2的屬性項值一樣臀蛛。
- 所創(chuàng)建Cookie的NAME和VALUE的值不能設(shè)置成非ASCII字符亲桦,如果要使用中文,可以通過URLEncoder將其編碼浊仆。
- 當NAME和VALUE的值出現(xiàn)一些TOKEN字符(如“\”客峭、“,”等)時抡柿,構(gòu)建返回頭會將該Cookie的Version自動設(shè)置為1舔琅。
- 當在該Cookie的屬性項中出現(xiàn)Version為1的屬性項時,構(gòu)建HTTP響應(yīng)頭同樣會將Version設(shè)置為1沙绝。
1.3 使用Cookie的限制
任何語言對Cookie的操作,其實都是讓瀏覽器對Cookie的操作鼠锈,Cookie的瀏覽器的特性闪檬,而瀏覽器對Cookie有數(shù)量限制(50個/每個域名),總大小限制(4096购笆,Chrome沒有這個限制)粗悯。
2 理解Session
前面已經(jīng)介紹了Cookie可以讓服務(wù)端程序跟蹤每個客戶端的訪問,但是每次客戶端的訪問都必須傳回這些Cookie同欠,如果Cookie很多样傍,則無形地增加了客戶端
與服務(wù)端的數(shù)據(jù)傳輸量横缔,而Session的出現(xiàn)正是為了解決這個問題。
同一個客戶端每次和服務(wù)端交互時衫哥,不需要每次都傳回所有的Cookie值茎刚,而是只要傳回一個ID,這個ID是客戶端第一次訪問服務(wù)端時生成的撤逢,而且每個客戶端是唯一的膛锭。
這樣每個客戶端就有了一個唯一的ID,客戶端只要傳回這個ID就行了蚊荣,這個ID通常是NAME為JSESIONID的一個Cookie初狰。
2.1 Session與Cookie
下面詳解講一下Session是如何基于Cookie來工作的。實際上有以下三種方式可以讓Session正常工作互例。
- 基于URL Path Parameter奢入,默認支持。
- 基于Cookie媳叨,如果沒有修改Context容器的Cookies標識腥光,則默認也是支持的。
- 基于SSL肩杈,默認不支持柴我,只有connector.getAttribute("SSLEnabled")為TRUE時才支持。
在第一種情況扩然,當瀏覽器不支持Cookie功能時艘儒,瀏覽器會將用戶的SessionCookieName重寫到用戶請求的URL參數(shù)中,傳遞格式如/path/Servlet;name=value;name2=value2?Name3=value3夫偶,其中“Servlet界睁;”后面的K-V就是要傳遞的Path Parameters,服務(wù)器會從這個Path Parameters中拿到用戶配置的SessionCookieName兵拢。關(guān)于這個SessionCookieName翻斟,如果在web.xml中配置session-config配置項,其cookie-config下的name屬性就是這個SessionCookieName的值说铃。如果沒有配置sessio-config配置項访惜,默認的SessionCookieName就是大家熟悉的“JSESSIONID”。需要說明的一點是腻扇,與Session關(guān)聯(lián)的Cookie與其他Cookie沒有什么不同债热。接著Request根據(jù)這個SessionCookieName到Parameters中拿到Session ID并設(shè)置到request.setRequestedSessionId中。
請注意幼苛,如果客戶端也支持Cookie窒篱,則Tomcat仍然會解析Cookie中的Session ID,并會覆蓋URL中的Session ID。
如果是第三種情況墙杯,則會根據(jù)javax.servlet.request.ssl_session屬性值設(shè)置Session ID配并。
2.2 Session如何工作
有了Session ID,服務(wù)端就可以創(chuàng)建HttpSession對象了高镐,第一次觸發(fā)通過request.getSession()
方法溉旋。如果當前的Session ID還沒有對應(yīng)的HttpSession對象,那么就創(chuàng)建一個新的避消,并將這個對象加到org.apache.catalina.Manager的session容器中保存低滩。Manager類將管理所有Session生命周期,Session過期將被回收岩喷,服務(wù)器關(guān)閉恕沫,Sessoin將被序列化到磁盤等。只要這個HttpSession對象存在纱意,用戶就可以根據(jù)Session ID來獲取這個對象婶溯,也就做到了對狀態(tài)的保持。

從Request中獲取的Session對象保存在org.apache.catalina.Manager類中偷霉,它的實現(xiàn)類是org.apache.catalina.session.StandardManager迄委,通過requestedSessionId從StandardManager的Sessions集合取出對應(yīng)的StandardSession對象。由于一個requestedSessionId對應(yīng)一個訪問的客戶端类少,所以一個客戶端也就對應(yīng)了一個StandardSession對象叙身,這個對象正是保存我們創(chuàng)建的Session值的。下面我們看一下StandardManager這個類是如何管理StandardSession的生命周期的硫狞。
StandardManager類負責Servlet容器中所有的StandardSession對象的生命周期管理信轿。當Servlet容器重啟或關(guān)閉時,StandardManager負責持久化沒有過期的StandardSession對象残吩,它會將所有的StandardSession對象持久化到一個以“SESSIONS财忽。ser”為文件名的文件中。到Servlet容器重啟時泣侮,也就是StandardManager初始化時即彪,它會重新讀取這個文件,解析出所有Session對象活尊,重新保存在StandardManager的sessions集合中隶校。
當Servlet容器關(guān)閉時StandardManager類會調(diào)用unload方法將session集合中的StandardSession對象寫到“SESSIONS.ser”文件中,然后在啟動時再重新恢復蛹锰,注意要持久化保存Servlet容器中的Session對象深胳,必須調(diào)用Servlet容器的stop的start命令,而不能直接結(jié)束(kill)Servlet容器的進程宁仔。
因為直接結(jié)束進程稠屠,Servlet容器沒有機會調(diào)用unload方法來持久化這些Session對象峦睡。
另外翎苫,在StandardManager的sessions集合中的StandardSession對象并不是永遠保存的权埠,否則Servlet容器的內(nèi)存將容易被消耗盡澳腹,所以必須給每個Session對象定義一個有效時間踩晶,超過這個時間則Session對象將被清除。在Tomcat中這個有效時間是60s(maxInactiveInterval屬性通知)政模,超過60s該Session將會過期呐粘。檢查每個Session是否失效是Tomcat的一個后臺線程中完成的满俗。
除了后臺進程檢查Session是否失效外,當調(diào)用request.getSession()
時也會檢查該Session是否過期作岖。值得注意的是唆垃,request.getSession()
方法調(diào)用的StandardSession永遠都會存在,即使與這個客戶端關(guān)聯(lián)的Session對象已經(jīng)過期痘儡。如果過期辕万,則又會重新創(chuàng)建一個全新的StandardSession對象,但是以前設(shè)置的Session值將會丟失沉删。如果你取到了Session對象渐尿,但是通過session.getAttribute
取不到前面設(shè)置的Session值,請不要奇怪矾瑰,因為很可能已經(jīng)失效了砖茸,請檢查以下<Manager pathname="" maxInactiveInterval="60" />中maxInactiveInterval
配置項的值,如果不想讓Session過期則可以設(shè)置為-1殴穴。但是你要仔細評估一下凉夯,網(wǎng)站的訪問量和設(shè)置的Session的大小,防止將你的Servlet容器內(nèi)存撐爆推正。如果不想自動創(chuàng)建Session對象恍涂,也可以通過request.getSession(bolean create)
方法來判斷與該客戶端關(guān)聯(lián)的Session對象是否存在。
3 Cookie安全問題
Cookie通過把所有要保存的數(shù)據(jù)通過HTTP的頭部從客戶端傳遞到服務(wù)端植榕,又從服務(wù)端傳回到客戶端再沧,所有的數(shù)據(jù)都存儲在客戶端的瀏覽器里,所以這些Cookie數(shù)據(jù)可以被訪問到尊残,通過瀏覽器插件可以對Cookie進行修改等炒瘸。
相對而言Session的安全性要高很多,因為Session是將數(shù)據(jù)保存在服務(wù)端寝衫,只是通過Cookie傳遞一個SessionID而已顷扩,所以Session更適合存儲用戶隱私和重要的數(shù)據(jù)。
4 分布式Session框架
4.1 Cookie存在哪些問題
- 客戶端Cookie存儲限制
- Cookie管理的混亂慰毅,每個應(yīng)用系統(tǒng)都自己管理每個應(yīng)用使用的Cookie會導致混亂隘截,由于通常應(yīng)用系統(tǒng)都在同一個域名下,Cookie又有上面一條提到的限制,所以統(tǒng)一管理很容易出現(xiàn)Cookie超出限制的情況婶芭。
- 不安全东臀,雖然通過設(shè)置HttpOnly屬性防止一些私密Cookie被客戶端訪問,但是仍然不能保證Cookie無法被篡改犀农。為了保證Cookie的私密性通常會對Cookie進行加密惰赋,但是維護這個加密Key也是一件麻煩的事情,無法保證定期更新加密Key也是會帶來安全性問題的一個重要因素呵哨。
4.2 Cookie+Session可以解決哪些問題
下面是分布式Session框架可以解決的問題:
- Session配置的統(tǒng)一管理赁濒。
- Cookie使用的監(jiān)控和統(tǒng)一規(guī)范管理。
- Session存儲的多元化孟害。
- Session配置的動態(tài)修改拒炎。
- Session加密key的定期修改。
- 充分的容災(zāi)機制挨务,保持框架的使用穩(wěn)定性枝冀。
- Session各種存儲的監(jiān)控和報警支持。
- Session框架的可擴展性耘子,兼容更多的Session機制如wapSession果漾。
- 跨域名Session與Cookie如何共享的問題。現(xiàn)在同一個網(wǎng)站可能存在多個域名谷誓,如何將Session和Cookie在不同的域名之間共享是一個具有挑戰(zhàn)性的問題绒障。
4.3 總體實現(xiàn)思路
為了達成上面所說的幾個目標,我們需要一個服務(wù)訂閱服務(wù)器捍歪,在應(yīng)用啟動時可以從這個訂閱服務(wù)器訂閱這個應(yīng)用需要的可寫Session項和可寫Cookie項户辱,這些配置的Session和Cookie可以限制這個應(yīng)用能夠使用哪些Session和Cookie,甚至可以通知Session和Cookie可讀或可寫糙臼。這樣可以精確地控制哪些應(yīng)用可以操作哪些Session和Cookie庐镐,可以有效控制Session的安全性和Cookie的數(shù)量。

如Session的配置項可以為如下形式:
<sessions>
<session>
<key>sessionID</key>
<cookiekey>sessionID</cookiekey>
<lifeCycle>9000</lifeCycle>
<base64>true</base64>
</session>
.......
</sessions>
Cookie的配置可以為如下形式:
<cookies>
<cookie>
<key>cookie</key>
<lifeCycle>10000</lifeCycle>
<type>1</type>
<path>/wp</path>
<domain>liwenguang.website</domain>
<decrypt>false</decrypt>
<httpOnly>false</httpOnly>
</cookie>
......
</cookies>
統(tǒng)一通過訂閱服務(wù)器推送配置可以有效地幾種管理資源变逃,所以可以省去每個應(yīng)用都來配置Cookie必逆,簡化Cookie的管理。如果應(yīng)用要使用一個新增的Cookie揽乱,則可以通過一個統(tǒng)一的平臺來申請名眉,申請通過才將這個配置項增加到訂閱服務(wù)器。如果是一個所有應(yīng)用都要使用的全局Cookie凰棉,那么只需要將這個Cookie通過訂閱服務(wù)器統(tǒng)一推送過去就行了损拢,省去了要在每個應(yīng)用中手動增加Cookie的配置。
關(guān)于這個訂閱服務(wù)器現(xiàn)在有很多開源的配置服務(wù)器撒犀,如ZooKeeper集群管理服務(wù)器福压,可以統(tǒng)一管理所有服務(wù)器的配置文件掏秩。
由于應(yīng)用是一個集群,所以不可能將創(chuàng)建的Session都保存在每臺應(yīng)用服務(wù)器的內(nèi)存中荆姆,因為如果每臺服務(wù)器有幾十萬的訪問用戶哗讥,那么服務(wù)器的內(nèi)存可能不夠用,即使內(nèi)存夠用胞枕,這些Session也無法同步到這個應(yīng)用的所有服務(wù)器中。所以要共享這些Session必須將它們存儲在一個分布式緩存中魏宽,可以隨時寫入和讀取腐泻,而且性能要很好才能滿足要求。當前能滿足這個要求的系統(tǒng)有很多队询,如MemCache或者淘寶的開源分布式緩存系統(tǒng)Tair都是很好的選擇派桩。
解決了配置和存儲問題,下面看一下如何存取Session和Cookie蚌斩。
既然是一個分布式Session的處理框架铆惑,必然會重新實現(xiàn)HttpSession的操作接口,使得應(yīng)用操作Session的對象都是我們實現(xiàn)的InnerHttpSession對象送膳,這個操作必須在進入應(yīng)用之前完成员魏,所以可以配置一個filter攔截用戶的請求。
先看一下如何封裝HttpSession對象和攔截請求叠聋,如下時序圖:

我們可以在應(yīng)用的web.xml中配置一個SessionFilter撕阎,用于在請求到達MVC框架之前封裝HttpServletRequest和HttpServletResponse對象,并創(chuàng)建我們自己的InnerHttpSession對象碌补,把它設(shè)置到request和response對象中虏束。這樣應(yīng)用系統(tǒng)通過request.getHttpSession()返回的就是我們創(chuàng)建的InnerHttpSession對象了,我們可以攔截response的addCookies設(shè)置的Cookie厦章。
在時序圖中镇匀,應(yīng)用創(chuàng)建的所有Session對象都會保存在InnerHttpSession對象中,當用戶的這次訪問請求完成時袜啃,Session框架將會把這個InnerHttpSession的所有內(nèi)容再更新到分布式緩存中汗侵,以便于這個用戶通過其它服務(wù)器再次訪問這個應(yīng)用系統(tǒng)。另外群发,為了保證一些應(yīng)用對Session穩(wěn)定性的特殊要求晃择,可以將一些非常關(guān)鍵的Session再存儲到Cookie中,如當分布式緩存存在問題時也物,可以將部分Session存儲到Cookie中宫屠,這樣即使分布式緩存出現(xiàn)問題也不會影響關(guān)鍵業(yè)務(wù)的正常運行。
4.4 增加Session跨域?qū)崿F(xiàn)
還有一個非常重要的問題就是如何處理跨域名來共享Cookie的問題滑蚯。我們知道Cookie是有域名限制的浪蹂,也就是在一個域名下的Cookie不能被另一個域名訪問抵栈,所以如果在一個域名下已經(jīng)登錄成功,如何訪問到另外一個域名的應(yīng)用且保證登錄狀態(tài)仍然有效坤次,對這個問題大型網(wǎng)站應(yīng)該經(jīng)常會遇到古劲。如何解決這個問題呢?
下面介紹一種處理方式缰猴,流程圖如下:

訪問域名A時服務(wù)器A獲得了session产艾,用戶訪問域名B時,如果發(fā)現(xiàn)服務(wù)器B沒有session滑绒,則302重定向跳轉(zhuǎn)到中轉(zhuǎn)服務(wù)器C(C你可以理解成專門取Session的域)闷堡,服務(wù)器C獲得了session后,則再進行302重定向到A服務(wù)器疑故,寫入session杠览,從而完成了session跨域。(如今纵势,大部分都是實現(xiàn)的單點登錄SSO踱阿,來解決子系統(tǒng)間的跨域問題,讓子系統(tǒng)共享頂級域名的Session钦铁、Cookie等)软舌。
5 Cookie壓縮
Cookie在HTTP的頭部,所以通常的gzip和deflate針對HTTP Body的壓縮不能壓縮Cookie牛曹,如果Cookie的量非常大葫隙,則可以考慮將Cookie也做壓縮,壓縮方式是將Cookie的多個k/v對看成普通的文本躏仇,做文本壓縮恋脚。壓縮算法同樣可以使用gzip和deflate算法,但是需要注意的一點是焰手,根據(jù)Cookie的規(guī)范糟描,在Cookie中不能包含控制字符,僅能包含ASCII碼為34~126的可見字符书妻。所以要將壓縮后的結(jié)果再進行轉(zhuǎn)碼船响,可以進行Base32或者Base64編碼。
// 使用DeflaterOutputStream壓縮后再用BASE64編碼
Cookie c = getCookieObject("");
HttpServletResponse res = getResponse();
ByteArrayOutputStream bos = new ByteArrayOutputStream();
DeflaterOutputStream dos = new DeflaterOutputStream(bos);
try {
dos.write(c.getValue().getBytes());
dos.close();
System.out.println("before compress length:" + c.getValue().length());
String compress = new sun.misc.BASE64Encoder().encode(bos.toByteArray());
res.addCookie(new Cookie("compress", compress));
System.out.println("after compress length:" + compress.getBytes().length);
} catch (IOException e) {
e.printStackTrace();
}
// 使用BASE64解碼后再用InflaterInputStream解壓
ByteArrayOutputStream out = new ByteArrayOutputStream();
try {
byte[] compress = new sun.misc.BASE64Decoder().decodeBuffer(new String(c.getValue().getBytes()));
ByteArrayInputStream bis = new ByteArrayInputStream(compress);
InflaterInputStream inflater = new InflaterInputStream(bis);
byte[] b = new byte[1024];
int count;
while ((count = inflater.read(b)) >= 0) {
out.write(b, 0, count);
}
inflater.close();
System.out.println(out.toByteArray());
} catch (IOException e) {
e.printStackTrace();
}
6 表單重復提交問題
要防止表單重復提交躲履,就要標識用戶的每一次訪問請求见间,使得每一次訪問對服務(wù)端來說都是唯一確定的。為了標識用戶的每次訪問請求工猜,可以在用戶請求一個表單域時增加一個隱藏表單項米诉,這個表單項每次都是唯一的token,如:

當用戶第一次請求表單頁面時生成唯一的token篷帅,并存儲到用戶Session中史侣,當用戶第二次請求表單頁面時再生成唯一的token拴泌,覆蓋Session,這樣就能保證每次都只能通過請求表單頁面來提交表單惊橱。
7 多終端Session統(tǒng)一
當前大部分網(wǎng)站都有了無線端蚪腐,對無線端的Cookie如何處理也是很多程序員必須考慮的問題。
在無線端發(fā)展初期税朴,后端的服務(wù)系統(tǒng)未必和PC的服務(wù)系統(tǒng)是統(tǒng)一的回季,這樣就涉及在一端調(diào)用多個系統(tǒng)時如何做到服務(wù)端Session共享的問題了。有兩個明顯的例子:
一個是在無線端可能會通過手機訪問無線服務(wù)端系統(tǒng)正林,如果它們兩個的登錄系統(tǒng)沒有統(tǒng)一的話泡一,將會非常麻煩,可能會出現(xiàn)二次登錄的情況卓囚;
另一個是在手機上登錄以后再在PC上同樣訪問服務(wù)端數(shù)據(jù),Session能否共享就決定了客戶端是否要再次登錄诅病。
針對這兩種情況哪亿,目前都有理由的解決方案。
- 多端共享Session
多端共享Session必須要做的工作是不管是無線端還是PC端贤笆,后端的服務(wù)系統(tǒng)必須統(tǒng)一會話架構(gòu)蝇棉,也就是兩邊的登錄系統(tǒng)必須要基于一致的會員數(shù)據(jù)結(jié)構(gòu)、Cookie與Session的統(tǒng)一芥永。也就是不管是PC端登錄還是無線端登錄篡殷,后面對應(yīng)的數(shù)據(jù)結(jié)構(gòu)和存儲要統(tǒng)一,寫到客戶端的Cookie也必須一樣埋涧,這是前提條件板辽。
那么如何做到這一點?就是要按照我們在前面所說的實現(xiàn)分布式的Session框架棘催。

上面服務(wù)端統(tǒng)一Session后劲弦,在同一個終端上不管是訪問哪個服務(wù)端都能做到登錄狀態(tài)統(tǒng)一。例如不管是Native還是內(nèi)嵌Webview醇坝,都可以拿統(tǒng)一的Session ID去服務(wù)端驗證登錄狀態(tài)邑跪。
- 多終端登錄
目前很多網(wǎng)站都會出現(xiàn)無線端和PC端多端登錄的情況,例如可以通過掃碼登錄等呼猪。這些是如何實現(xiàn)的呢画畅?
這里手機端在掃碼之前必須是已經(jīng)登錄的狀態(tài),因為這樣才能獲取到到底是誰將要登錄的信息宋距,同時掃碼的二維碼也帶有一個特定的標識轴踱,標識是這個客戶端
通過手機端登陸了。當手機端掃碼成功后谚赎,會在服務(wù)端設(shè)置這個二維碼對應(yīng)的標識為已經(jīng)登錄成功寇僧,這時PC客戶端會通過將“心跳”請求發(fā)送到服務(wù)端摊腋,來驗證是否已經(jīng)登錄成功,這樣就稱為一種便捷的登錄方式嘁傀。(博主以微信掃碼登錄為例兴蒸,每次微信PC端的二維碼都是帶有一個唯一的標識,當你用登錄的手機微信掃碼之后细办,手機將你已登錄的微信信息獲取橙凳,并發(fā)送給微信服務(wù)端,微信服務(wù)端將二維碼的唯一標識以及手機微信的賬號信息綁定笑撞,發(fā)送給微信PC端岛啸,其中,微信PC二維碼客戶端類似Watch了某個ZK的節(jié)點進行監(jiān)聽茴肥,這樣避免客戶端每隔一段時間發(fā)送心跳)坚踩。
8 總結(jié)
Cookie和Session都是為了保持用戶訪問的連續(xù)狀態(tài),之所以要保持這種狀態(tài)瓤狐,一方面是為了方便業(yè)務(wù)實現(xiàn)瞬铸,另一方面就是簡化服務(wù)端的程序設(shè)計,提高訪問性能础锐,但是也帶來了安全問題嗓节、應(yīng)用的分布式部署帶來的Session的同步問題以及跨域名Session的同步問題(通過單點登錄避免)。