Cookie與Session

Cookie

        Cookie是由服務端生成的,發送給客戶端(通常是瀏覽器)的,保存在客戶端中,並且是可見的,客戶端的一些程序可能會篡改、窺探cookie中的內容。假如用到Cookie,比較好的方法是,敏感的信息如賬號密碼等儘量不要寫到Cookie中。最好是像Google、Baidu那樣將Cookie信息加密,提交到服務器後再進行解密,保證Cookie中的信息只要本人能讀得懂。(選擇Session就省事多了,反正是放在服務器上,Session裏任何隱私都能夠有效的保護)

按在客戶端中的存儲位置,可分爲內存Cookie和硬盤Cookie:

  1. 內存Cookie由瀏覽器維護,保存在內存中,瀏覽器關閉後就消失了,其存在時間是短暫的。
  2. 硬盤Cookie保存在硬盤裏,有一個過期時間,除非用戶手工清理或到了過期時間,硬盤Cookie不會被刪除,其存在時間是長期的。所以,按存在時間,可分爲非持久Cookie和持久Cookie。

        登錄過Google,Google的登錄信息長期有效。用戶不用每次訪問都重新登錄,Google會持久地記載該用戶的登錄信息。要到達這種效果,運用Cookie會是比較好的選擇。只需要設置Cookie的過期時間屬性爲一個很大很大的數字。當用戶第一次訪問並登陸一個網站的時候,cookie的設置以及發送會經歷以下4個步驟:

  1. 客戶端發送一個請求到服務器 
  2. 服務器返回一個HttpResponse響應到客戶端,其中設置了Set-Cookie的頭部(可以在服務端設置cookie.setHttpOnly(true)設置響應頭攜帶cookie信息)
  3.  客戶端保存cookie,之後向服務器發送請求時,HttpRequest請求中會包含一個Cookie的頭部
  4. 服務器返回響應數據
package com.cloud.ceres.rnp.control.web;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.util.Enumeration;


@RestController
@RequestMapping("/aop")
public class AopControl {
  
    private static Logger logger = LoggerFactory.getLogger (AopControl.class);

    @GetMapping("/queryXXX")
    public String queryXXX()  {
        ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = requestAttributes.getRequest();
        HttpServletResponse response = requestAttributes.getResponse();
        Cookie[] cookies = request.getCookies();
        if (cookies!= null) {
            for (Cookie cookie : cookies) {
                System.out.println(cookie.getName() + ":[cooke]:" + cookie.getValue());
                cookie.setHttpOnly(true);
                response.addCookie(cookie);
            }
        }
        HttpSession session = request.getSession();
        System.out.println("sessionId:" + session.getId());
        Enumeration<String> attrs = session.getAttributeNames();
        while(attrs.hasMoreElements()){
            // 獲取session鍵值
            String name = attrs.nextElement().toString();
            // 根據鍵值取session中的值
            Object vakue = session.getAttribute(name);
            System.out.println(name + ":[session]" + vakue);
        }
        return "aopTest方法結束";
    }

}

通過瀏覽器訪問該接口

第一次訪問:由於第一次是不存在cookie的,所以第一次訪問控制檯輸出如下:

sessionId:5FCF280DDF6F28C70C178B1FCBF12A01

第二次訪問:由於第一次訪問,所以第一次訪問控制檯輸出如下:

JSESSIONID:[cooke]:F9AF6D66D4BAC7B3033A5F29F838D033
sessionId:5FCF280DDF6F28C70C178B1FCBF12A01

        實際上大多數的應用都是用 Cookie 來實現Session跟蹤的,第一次創建Session的時候(服務點自動會生成持久化保存在服務端),服務端會在HTTP協議中告訴客戶端,需要在 Cookie 裏面記錄一個Session ID,以後每次請求把這個會話ID發送到服務器,我就知道你是誰了。如果客戶端的瀏覽器禁用了 Cookie 怎麼辦?一般這種情況下,會使用一種叫做URL重寫的技術來進行會話跟蹤,即每次HTTP交互,URL後面都會被附加上一個諸如 sid=xxxxx 這樣的參數,服務端據此來識別用戶。

(一)Cookie的根本作用就是在客戶端存儲用戶訪問網站的一些信息。典型的應用有:

1、記住密碼,下次自動登錄。

2、購物車功能。

3、記錄用戶瀏覽數據,進行商品(廣告)推薦。

(二)缺陷

①Cookie會被附加在每個HTTP請求中,所以無形中增加了流量。

②由於在HTTP請求中的Cookie是明文傳遞的,所以安全性成問題。(除非用HTTPS)

③Cookie的大小限制在4KB左右。對於複雜的存儲需求來說是不夠用的。

參考:https://www.jianshu.com/p/6fc9cea6daa2

Session        

        在說session是啥之前,我們先來說說爲什麼會出現session會話,它出現的機理是什麼?我們知道,我們用瀏覽器打開一個網頁,用到的是HTTP協議,學過計算機的應該都知道這個協議,它是無狀態的,什麼是無狀態呢?就是說這一次請求和上一次請求是沒有任何關係的,互不認識的,沒有關聯的。但是這種無狀態的的好處是快速。

        所以就會帶來一個問題就是,我希望幾個請求的頁面要有關聯,比如:我在a.html裏面登陸了,我在b.html 也希望是登陸狀態,但是,這是2個不同的頁面,也就是2個不同的HTTP請求,這2個HTTP請求是無狀態的,也就是無關聯的,所以無法單純的在a.html中讀取到它在b.html中已經登陸了!那咋搞呢?我不可能這2個頁面我都去登陸一遍吧。或者用笨方法這2個頁面都去查詢數據庫,如果有登陸狀態,就判斷是登陸的了。這種查詢數據庫的方案雖然可行,但是每次都要去查詢數據庫不是個事,會造成數據庫的壓力。

        所以正是這種訴求,這個時候,一個新的客戶端存儲數據方式出現了:cookie。cookie是把少量的信息存儲在用戶自己的電腦上,它在一個域名下是一個全局的,只要設置它的存儲路徑在域名www.a.com下 ,那麼當用戶用瀏覽器訪問時,就可以從這個域名的任意頁面讀取cookie中的信息。所以就很好的解決了我在b.html頁面登陸了,b.html獲取到這個登陸信息了。同時又不用反覆去查詢數據庫。雖然這種方案很不錯,也很快速方便,但是由於cookie 是存在用戶端,而且它本身存儲的尺寸大小也有限,最關鍵是用戶可以是可見的(密碼一般不會存於cookie),並可以隨意的修改,很不安全。那如何又要安全,又可以方便的全局讀取信息呢?於是,這個時候,一種新的存儲會話機制:session 誕生了

        session的全部機制也是基於這個session_id,它用來區分哪幾次請求是一個人發出的。爲什麼要這樣呢?因爲HTTP是無狀態無關聯的,一個頁面可能會被成百上千人訪問,而且每個人的用戶名是不一樣的,那麼服務器如何區分這次是小王訪問的,那次是小李訪問的呢?所以就有了找個唯一的session_id 來綁定一個用戶。一個用戶在一次會話上就是一個session_id,這樣成千上萬的人訪問,服務器也能區分到底是誰在訪問了。

        每次我們訪問一個頁面,默認會自動生成一個session_id 來標註是這次會話的唯一ID,同時也會自動往cookie裏寫入一個名字爲JSESESSID的變量(tomcat生成取名爲jseSessionId),它的值正是session_id,當這次會話沒結束,再次訪問的時候,服務器會去讀取這個JSESESSID的cookie是否有值有沒過期,如果能夠讀取到,則繼續用這個session_id,如果沒有,就會新生成一個session_id,同時生成JSESESSID這個cookie。由於默認生成的這個JSESESSID cookie是會話,也就是說關閉瀏覽器就會過期掉,所以,下次重新瀏覽時,會重新生成一個session_id。

        好,這個是session_id,就用來標識綁定一個用戶的,既然session_id生成了。那麼當我們往session裏面寫入數據即可。session是如何保存的?Tomcat保存session的方式是服務器端的內存中。對於PHP而言是保存在文件中。上述有提及。

(一)存儲用戶信息,典型的應用有:

1、購物車功能。(每次加入購物車攜帶session_id傳入到服務器,然後在比較服務器中的session_id(此時一定要session共享),然後重新put新的list到對應的購物車集合中去)

2、登陸狀態(將session存於redis,給其設置過期時間,如果過期了則視爲離線,需重新登陸)

(二)缺陷

①Cookie會被附加在每個HTTP請求中,所以無形中增加了流量。

②由於在HTTP請求中的Cookie是明文傳遞的,所以安全性成問題。(除非用HTTPS)

③Cookie的大小限制在4KB左右。對於複雜的存儲需求來說是不夠用的。

備註

  1. 除了上述提及的方法外,解決session共享問題還有這樣一種方式:(實質上並不是通過session的共享來解決的)這裏以nginx爲例,將用戶請求分發到了不同機器上,那麼我們只需要固定,同一用戶請求分發到同一機器上進行處理,即這一次用戶來請求服務器了,那麼下一次它再來的時候,同樣也請求也被分發到與上一次相同的服務器。這樣就確保了同一用戶不會因爲請求分發到不同機器上而獲取不到session數據的問題了。
  2. 從瀏覽器打開訪問了某一個網站,關閉瀏覽器。這樣的操作我們算一次“會話”。所以大部分就會認爲用戶訪問了網站就會產生session ID。實際上不然。例如:在Java中我們需要調用HttpServletRequest的getSession方法創建session。而在PHP中需要session_start()一下,服務器纔會將存有session ID的cookie回傳回去。否則不會有什麼session產生。
  3. session不會因爲瀏覽器的關閉而刪除。但是存有session ID的cookie的默認過期時間是會話級別。也就是用戶關閉了瀏覽器,那麼存儲在客戶端的session ID便會丟失,但是存儲在服務器端的session數據並不會被立即刪除。從客戶端即瀏覽器看來,好像session被刪除了一樣(因爲我們丟失了session ID,找不到原來的session數據了)。

疑問?

        當用戶關閉瀏覽器後,該cookie應該就會自動銷燬的。下次用戶再次訪問該網頁,該cookie應該是不復存在了。但是谷歌瀏覽器關閉後,這個PHPSESSID 還存在呢? 比較疑惑,在火狐和IE都測試過,沒有這樣的問題???

 

參考:https://blog.csdn.net/qq_15096707/article/details/74012116

          https://www.cnblogs.com/isme-zjh/p/11359557.html

          https://www.zhihu.com/question/19786827/answer/28752144
 

獲取cookie與session

        目前我們項目大多數是基於Spring框架的,所以獲取它只需要拿到HttpServletRequest這個類即可,這個類裏面可以從中拿到前臺的cookie 和 服務器的 session(sessionId) 以及傳入參數

1、RequestContextHolder(org.springframework.web.context.request.RequestContextHolder)

ServletRequestAttributes requestAttributes = (ServletRequestAttributes)                     RequestContextHolder.getRequestAttributes();
HttpServletRequest request = requestAttributes.getRequest();
Cookie[] cookies = request.getCookies();
HttpSession session = request.getSession();
System.out.println("sessionId:" + session.getId());
Enumeration<String> attrs = session.getAttributeNames();
while(attrs.hasMoreElements()){
   // 獲取session鍵值
   String name = attrs.nextElement().toString();
   // 根據鍵值取session中的值
   Object vakue = session.getAttribute(name);
   System.out.println("------" + name + ":" + vakue +"--------\n");
}

2、ServletActionContextcom.opensymphony.webwork.ServletActionContext)             

 HttpServletRequest request2 =ServletActionContext.getRequest();
 HttpSession session2 = request.getSession();

 

發佈了78 篇原創文章 · 獲贊 18 · 訪問量 1萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章