java開發(fā)web前端cookie session及token會話機制詳解
而web系統(tǒng)開發(fā)者就好比一個醫(yī)術(shù)精湛的醫(yī)生,醫(yī)生需要十分清楚人體的經(jīng)絡(luò)和血液流向才能對癥下藥,而web系統(tǒng)開發(fā)者需要十分清楚cookie、session機制,才能迅速解決疑難BUG,開發(fā)出更好的web系統(tǒng)?!f
引入:
我們都知道http協(xié)議本身是一種無狀態(tài)的協(xié)議,一個普通的請求大致分為三步:
1、客戶端發(fā)送請求給服務(wù)器
2、服務(wù)器處理該請求
3、服務(wù)器將處理結(jié)果響應(yīng)該客戶端。
之后該客戶端再次向該服務(wù)區(qū)發(fā)送請求后,服務(wù)器端并不能知道這兩個請求是否是同一個瀏覽器或用戶發(fā)出來的。所以作為web服務(wù)器必須能夠采用某種方式來唯一識別同一個用戶,并記錄該用戶的狀態(tài)。而這同一個客戶端與服務(wù)器端的在一段時間內(nèi)的多次交互,我們就可以稱該客戶端為該服務(wù)器端的一個客戶端會話窗口,有了會話窗口,我們就能確定哪個請求是哪個用戶發(fā)出的了,從而可以實現(xiàn)會話跟蹤,并記錄用戶的行為。
概念:
會話:可以理解為用戶打開瀏覽器,訪問該web服務(wù)器的多個資源,然后關(guān)閉瀏覽器,這中間的一系列過程稱之為一個會話。
有狀態(tài)的會話:瀏覽器發(fā)送的每一次請求,每一個會話都要有唯一的標識來唯一標識自己,當瀏覽器發(fā)送請求的時候就帶上這個標識來讓服務(wù)器識別,從而實現(xiàn)有“狀態(tài)”的會話。
javaweb中有兩種實現(xiàn)會話的機制:
1)Cookie機制
2)Session機制
PS:cookie和session是瀏覽器與服務(wù)器交互的一種規(guī)范,有專門的的組織對該規(guī)范進行定義,只要瀏覽器或服務(wù)器遵守了該規(guī)范,我們就能使用cookie和session。其他能做web開發(fā)的高級語言也有,只是實現(xiàn)方式不同罷了。
一、cookie機制
1、基本介紹
1)cookie機制采用的是在客戶端保持 HTTP 狀態(tài)信息的方案。當瀏覽器訪問WEB服務(wù)器的某個資源時,WEB服務(wù)器會在HTTP響應(yīng)頭中添加一個鍵值對傳送給瀏覽器,再由瀏覽器將該cookie放到客戶端磁盤的一個文件中,該文件可理解為cookie域(鍵值對的集合),往后每次訪問某個網(wǎng)站時,都會在請求頭中帶著這個網(wǎng)站的所有cookie值。(至于怎么區(qū)分不同網(wǎng)站的cookie的,很簡單,每個網(wǎng)站都給他一個唯一標識比如網(wǎng)址等,每次打開某網(wǎng)址時,就查詢該網(wǎng)站下的所有cookie值即可。)
2)每一個cookie都有一個name和一個value,且name是唯一的。相同名字時,后者會覆蓋掉前者(類似哈希表的key的效果)。
3)一個WEB瀏覽器也可以存儲多個WEB站點提供的Cookie。瀏覽器一般只允許存放300個Cookie,每個站點最多存放20個Cookie,每個Cookie的大小限制為4KB。
2、分類
1)會話級別的cookie
默認情況下它是一個會話級別的cookie,存儲在瀏覽器的內(nèi)存中,用戶退出瀏覽器之后被刪除。
2)持久化的cookie
若希望瀏覽器將該cookie存儲在磁盤上,則需要設(shè)置該cookie的生命周期setMaxAge,并給出一個以秒為單位的時間。將最大時效設(shè)為0則是命令瀏覽器刪除該cookie。
3、cookie的作用域
cookie的domain和path屬性定義了cookie的作用范圍,即訪問哪些網(wǎng)站或url時,會自動的帶著該cookie。domain即域名,默認是當前主機(不包括子域名),path默認是*(所有路徑),即域名后面的的路徑。大部分情況下我們都是使用默認的設(shè)置即可。
4、基本原理
當一個瀏覽器訪問某web服務(wù)器時,web服務(wù)器會調(diào)用HttpServletResponse的addCookie()方法,在響應(yīng)頭中添加一個名叫Set-Cookie的響應(yīng)字段用于將Cookie返回給瀏覽器,當瀏覽器第二次訪問該web服務(wù)器時會自動的將該cookie回傳給服務(wù)器,來實現(xiàn)用戶狀態(tài)跟蹤。
5、常用API
javax.servlet.http.Cookie類來封裝Cookie信息,它包含有生成Cookie信息和提取Cookie信息的各個屬性的方法。
1)public Cookie(String name,String value)
2)setMaxAge(int longTime)與getMaxAge方法:設(shè)置和獲取cookie的最大有效時長。setMaxAge(0) 表示刪除磁盤上的某個cookie。
注意:
cookie沒有提供修改方法,當name一樣時,覆蓋原來的就算是更新了。
刪除也是,setMaxAge(0),當name一樣時,原來的會被覆蓋掉,新建的沒有生命周期,也會被立馬刪除。
3)setPath與getPath方法 :設(shè)置或讀取Cookie的作用范圍。
4)HttpServletResponse接口中定義了一個addCookie(Cookie cookie)方法,它用于在發(fā)送給瀏覽器的HTTP響應(yīng)消息中增加一個Set-Cookie響應(yīng)頭字段。
5)HttpServletRequest接口中定義了一個getCookies方法,它用于從HTTP請求消息的Cookie請求頭字段中讀取所有的Cookie項。
6)getName方法 :獲取到cookie的name。
7)setValue(String value)與getValue方法:設(shè)置和獲取cookie的value。
6、基本應(yīng)用
自動登錄、跟蹤用戶上次訪問站點的時間、顯示最近瀏覽信息等。
這是我之前寫的一個使用cookie進行自動登錄的服務(wù)器代碼,很早了都。
//如果cookie中有customer信息,就放到session中 boolean checkCustomerCookie(HttpServletRequest request) throws UnsupportedEncodingException { Cookie[] cookies = request.getCookies(); if (cookies != null) { for (Cookie cookie : cookies) { String cookieName = cookie.getName(); //如果有,解密后拿cookie中的值和數(shù)據(jù)庫中的值進行比較 if (Constant.cookieCustomerKey.getName().equals(cookieName)){ String cookieValue = cookie.getValue(); String decry = EncrypUtils.Base64Util.decry(cookieValue); Customer customer1 = JsonUtils.stringToObject(decry, Customer.class); Customer customer2 = customerService.checkLogin(customer1.getPhoneNumber(), customer1.getPassword()); if (customer2 != null){ //放入到session中,放行 request.getSession().setAttribute("customer",customer2); //自動登錄時,更新用戶的在線狀態(tài) Customer onlineCustomer = new Customer(); onlineCustomer.setId(customer2.getId()); onlineCustomer.setOnlineStatus(String.valueOf(Constant.ONLINESTATUS.getCode())); customerService.updateById(onlineCustomer); return true; } } } } return false; }
7、Cookie中存儲中文問題
cookie中存儲中文會出現(xiàn)中文亂碼,需要對value進行額外的編碼
1、base64編碼
存儲:Base64.getEncoder().encodeToString(content.getBytes("utf-8"));
讀?。簄ew String(Base64.getDecoder().decode(cookie.getValue()),"utf-8")
2、URLEncoder類
存儲:Cookie cookie = new Cookie("userName", URLEncoder.encode("你好世界", "UTF-8"));
讀?。篣RLDecoder.decode(cookie.getValue(), "UTF-8")
二、session機制
1、基本介紹
session機制采用的是在服務(wù)器端保持 HTTP 狀態(tài)信息的方案。為了加速session的讀取和存儲,web服務(wù)器中會開辟一塊內(nèi)存用來保存服務(wù)器端所有的session,每個session都會有一個唯一標識sessionid,根據(jù)客戶端傳過來的jsessionid(cookie中),找到對應(yīng)的服務(wù)器端的session。為了防止服務(wù)器端的session過多導致內(nèi)存溢出,web服務(wù)器默認會給每個session設(shè)置一個有效期, (30分鐘)若有效期內(nèi)客戶端沒有訪問過該session,服務(wù)器就認為該客戶端已離線并刪除該session。
保存sessionID的方式
1)cookie中
通過一個特殊的cookie,name為JSESSIONID,value為服務(wù)器端某個 session的ID,默認的方式。但是當瀏覽器禁用cookie后session就會失效。
2)url重寫
當瀏覽器Cookie被禁時用。
就是把session的id附加在URL路徑的后面。附加的方式也有兩種,一種是作為URL路徑的附加信息,另一種是作為查詢字符串附加在URL后面。
做法:
1、response.encodeURL(String url)用于對表單action和超鏈接的url地址進行重寫
2、response.encodeRedirectURL(String url) 用于對sendRedirect方法后的url地址進行重寫。
這兩個方法很智能,若瀏覽器禁用了cookie,就默認會進行url重寫(url中帶上sessionid),當用戶瀏覽器沒有禁用cookie時,就不在URL后附加sessionid。
用法就是代替response.sendRedirect(String url)。、
2、基本原理
當用戶發(fā)送一個請求到服務(wù)器端時,服務(wù)器會先檢查請求中是否含有sessionid(存在cookie中或者在url中),
》》如果不存在sessionid(說明是第一次請求),就會為該請求用戶創(chuàng)建一個session對象,并將該session對象的sessionid(放到響應(yīng)頭的set-cookie中,格式set-cookie:sessionid,下次再請求時cookie中就會有一個name為jsessionid的cookie,value就是sessionid)響應(yīng)給客戶端。
》》如果存在sessionid,就會在服務(wù)器端查找是否有該sessionid對應(yīng)的session,如果有就使用,沒有就創(chuàng)建一個。
所以說,服務(wù)器端的session和客戶端的cookie是息息相關(guān)的,若是沒有了cookie,又不做其他處理的話,服務(wù)器端的session也沒了。
3、常用API
getId()方法:得到sessionid。
invalidate()方法:讓session立刻失效。
getAttribute(String key):根據(jù)key獲取該session中的value。
setAttribute(String key,Object value):往session中存放key-value。
removeAttribute(Stringkey):根據(jù)key刪除session中的key-value。
getServletContext():得到ServletContext。
setMaxInactiveInterval(long timeout)/getMaxInactiveInterval:設(shè)置/獲取session的最大有效時間。
getCreationTime方法:獲取session的創(chuàng)建的時間。
getLastAccessedTime方法:獲取session最后一次訪問的時間。
getSession():從HttpServletRequest中獲取session。
4、基本應(yīng)用 跨瀏覽器的會話跟蹤
因為cookie在多個瀏覽器之間是共享的(但是不能跨域),所以可以將sessionid存在cookie中,再把cookie存入磁盤中,然后在其他瀏覽器中再次訪問該服務(wù)器時,就會讀取到cookie中的sessionid,從而回到上次訪問的頁面了。
一段示例代碼:
session.setMaxInactiveInterval(2*3600);//session 保存?zhèn)z小時 Cookie cookie=new Cookie("JSESSIONID",session.getId());//sessionid放到cookie中 cookie.setMaxAge(2*3600);//客戶端的cookie也保存?zhèn)z小時 cookie.setPath("/");//cookie作用范圍設(shè)為整個項目 response.addCookie(cookie);//給瀏覽器返回該Cookie
5、常見問題
1、關(guān)閉瀏覽器后cookie會消失嗎?
答:看情況。
經(jīng)過上面關(guān)于cookie的分析之后我們知道,cookie默認是存在于瀏覽器內(nèi)存中的,若此時cookie沒有持久化,瀏覽關(guān)閉后cookie會消失;若此時cookie進行了持久化,瀏覽器關(guān)閉后cookie不會消失。
2、關(guān)閉瀏覽器后session會消失嗎?
答:看起來會實際上不會。
這個問題需要從以下兩個方面考慮:
1)從服務(wù)器端考慮
我們知道session是存在于服務(wù)器端內(nèi)存中的,和瀏覽器沒有關(guān)系,所以瀏覽器關(guān)閉后,服務(wù)器端的session不會消失。(除非服務(wù)器重啟或session達到了過期時間)
2)從瀏覽器端考慮
我們知道瀏覽器是根據(jù)cookie中的jesessionid值來唯一找到服務(wù)器端的session的,此時若cookie沒有持久化,瀏覽器關(guān)閉后cookie也跟著消失。所以當用戶再次打開瀏覽器后,由于沒有了cookie中的jesessionid,自然也無法唯一找到服務(wù)器端的session,對用戶來說,確實是瀏覽器關(guān)閉后再次打開就無法找到上次的會話了,誤以為是關(guān)閉瀏覽器后服務(wù)器端的session也跟著消失,其實還在。
三、token
1、token是啥?
token,可以翻譯成"令牌",本質(zhì)上它是一個全局唯一的字符串,用來唯一識別一個客戶端。但它不像cookie和session一樣是一種web規(guī)范,個人認為他是借鑒了cookie和session工作的原理,進而延伸出來的一種維持用戶會話狀態(tài)的機制。
2、token解決了什么問題?
token解決了session依賴于單個Web服務(wù)器的問題。單體應(yīng)用時用戶的會話信息保存在session中,session存在于服務(wù)器端的內(nèi)存中,由于前前后后用戶只針對一個web服務(wù)器,所以沒啥問題。但是一到了web服務(wù)器集群的環(huán)境下(我們一般都是用Nginx做負載均衡,若是使用了輪詢等這種請求分配策略),就會導致用戶小a在A服務(wù)器登錄了,session存在于A服務(wù)器中,但是第二次請求被分配到了B服務(wù)器,由于B服務(wù)器中沒有用戶小a的session會話,導致用戶小a還要再登陸一次,以此類推。這樣用戶體驗很不好。當然解決辦法也有很多種,比如同一個用戶分配到同一個服務(wù)處理、使用cookie保持用戶會話信息等。
我們今天討論的是用戶會話信息集中存儲的這種方案。類比之前cookie和session的機制,在請求中根據(jù)cookie中的jesessionid來自動找到服務(wù)器端的session,從而從session中取出當前用戶的會話信息。
Session session = request.getSession();// 獲取session session.getAttribute("user") // 獲取session中的用戶信息
3、我們可以模擬cookie和session的這種機制:
①、cookie中是根據(jù)jesessionid來找到服務(wù)器端的session的,jesessionid就是一個全局唯一的隨機字符串,我們也可以生成一個全局唯一的字符串比如使用UUID或時間戳加隨機字符串的形式;
②、web服務(wù)器在內(nèi)存中存儲所有的session,每個session都有一個唯一的id標識,value就是session本身,session里面又有好多鍵值對。數(shù)據(jù)在內(nèi)存中、key-value形式的、value里面又有好多鍵值對,這簡直就是對redis的哈希表十分準確的描述啊,所以我們可以使用redis的哈希類型來模型服務(wù)器端的session。
③、有了客戶端的隨機字符串,有了服務(wù)器端的會話信息存儲,接下來就讓他們匹配起來就完事了,next:
cookie和session機制中是根據(jù)cookie中的jesessionid來自動找到服務(wù)器端的session,說是自動查找,其實是我們調(diào)用了他封裝好的方法request.getSession()獲取的,這個request中就包含了本次請求的所有信息,而調(diào)用的getSession()方法中,肯定做了這件事——獲取請求頭中cookie的jesessionid的值,根據(jù)這個值到服務(wù)器的內(nèi)存中找到對應(yīng)的session并返回。所以我們也完全可以模仿它這種機制:我們可以在用戶第一次請求該web服務(wù)器時或是用戶登錄該web服務(wù)器時,生成一個全局唯一的token返回給前端存儲,同時將該用戶信息存到redis中并設(shè)置有效期,之后每次請求中都在請求頭中帶著這個token,服務(wù)器端根據(jù)這個token到redis中查找對應(yīng)的用戶信息,即得到了我們所說的 "session"。
客戶端的token我們可以這樣傳:
$.ajax({ headers:{"token":localStorage.getItem('token')}, type: 'get', url:'/xxx/xxx/xxx', dataType: 'json', success: function(we) { // some code }); },
服務(wù)器端的用戶信息我們可以這樣獲取:
/** * 返回當前用戶 * @return */ public User getCurrUser(){ ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes)RequestContextHolder.getRequestAttributes(); String token = servletRequestAttributes.getRequest().getHeader("token"); String strUser = RedisUtils.get(token); return JsonUtils.stringToObject(strUser,User.class); }
ok,關(guān)于cookie、session和token暫時就這么多,能看到這兒也不容易,點個贊或評論一下再走,順便給自己也加點經(jīng)驗值。 這種雙贏的事情以后要多做啊~希望大家以后多多支持腳本之家!
相關(guān)文章
Java Date與String的相互轉(zhuǎn)換詳解
這篇文章主要介紹了Java Date與String的相互轉(zhuǎn)換詳解的相關(guān)資料,需要的朋友可以參考下2017-02-02Serializable接口的作用_動力節(jié)點Java學院整理
這篇文章主要為大家詳細介紹了java中Serializable接口的作用,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-05-05Java 數(shù)據(jù)結(jié)構(gòu)七大排序使用分析
這篇文章主要介紹了Java常用的排序算法及代碼實現(xiàn),在Java開發(fā)中,對排序的應(yīng)用需要熟練的掌握,這樣才能夠確保Java學習時候能夠有扎實的基礎(chǔ)能力。那Java有哪些排序算法呢?本文小編就來詳細說說Java常見的排序算法,需要的朋友可以參考一下2022-04-04mybatis中方法返回泛型與resultType不一致的解決
這篇文章主要介紹了mybatis中方法返回泛型與resultType不一致的解決,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-07-07java selenium使用瀏覽器調(diào)試工具實現(xiàn)方法
本文主要介紹java selenium使用瀏覽器調(diào)試工具,這里整理了幾種瀏覽器的調(diào)試方法,有需要的小伙伴可以參考下2016-08-08Java中ThreadLocal?導致內(nèi)存?OOM?的原因分析
這篇文章主要介紹了Java中ThreadLocal導致內(nèi)存OOM的原因分析,文章基于Java的相關(guān)內(nèi)容展開ThreadLocal導致內(nèi)存OOM的原因分析,需要的小伙v阿布可以參考一下2022-05-05