日韩黑丝制服一区视频播放|日韩欧美人妻丝袜视频在线观看|九九影院一级蜜桃|亚洲中文在线导航|青草草视频在线观看|婷婷五月色伊人网站|日本一区二区在线|国产AV一二三四区毛片|正在播放久草视频|亚洲色图精品一区

分享

不了解Cookie、Session、Token?一文給你整明白

 板橋胡同37號 2019-12-14

有很多小伙伴還搞不清 Cookie、Session、Token 他們的區(qū)別,今天我就帶大家徹底搞懂他們。


圖片來自 Pexels


Cookie


夏洛:大爺,樓上322住的是馬冬梅家吧?
大爺:馬都什么?
夏洛:馬冬梅。
大爺:什么都沒啊?
夏洛:馬冬梅啊。
大爺:馬什么沒?
夏洛:行,大爺你先涼快著吧。
在了解這三個概念之前我們先要了解 HTTP 是無狀態(tài)的 Web 服務器,什么是無狀態(tài)呢?

就像上面夏洛特煩惱中經(jīng)典的一幕對話一樣,一次對話完成后下一次對話完全不知道上一次對話發(fā)生了什么。
如果在 Web 服務器中只是用來管理靜態(tài)文件還好說,對方是誰并不重要,把文件從磁盤中讀取出來發(fā)出去即可。
但是隨著網(wǎng)絡的不斷發(fā)展,比如電商中的購物車只有記住了用戶的身份才能夠執(zhí)行接下來的一系列動作。所以此時就需要我們無狀態(tài)的服務器記住一些事情。
那么 Web 服務器是如何記住一些事情呢?既然 Web 服務器記不住東西,那么我們就在外部想辦法記住,相當于服務器給每個客戶端都貼上了一個小紙條。
上面記錄了服務器給我們返回的一些信息。然后服務器看到這張小紙條就知道我們是誰了。那么 Cookie 是誰產(chǎn)生的呢?Cookies 是由服務器產(chǎn)生的。

接下來我們描述一下 Cookie 產(chǎn)生的過程:
  • 瀏覽器第一次訪問服務端時,服務器此時肯定不知道他的身份,所以創(chuàng)建一個獨特的身份標識數(shù)據(jù),格式為 key=value,放入到 Set-Cookie 字段里,隨著響應報文發(fā)給瀏覽器。

  • 瀏覽器看到有 Set-Cookie 字段以后就知道這是服務器給的身份標識,于是就保存起來,下次請求時會自動將此 key=value 值放入到 Cookie 字段中發(fā)給服務端。

  • 服務端收到請求報文后,發(fā)現(xiàn) Cookie 字段中有值,就能根據(jù)此值識別用戶的身份然后提供個性化的服務。

接下來我們用代碼演示一下服務器是如何生成,我們自己搭建一個后臺服務器,這里用的是 Spring Boot 搭建的,并且寫入 SpringMVC 的代碼如下:

@RequestMapping('/testCookies')
public String cookies(HttpServletResponse response){
    response.addCookie(new Cookie('testUser','xxxx'));
    return 'cookies';
}
項目啟動以后我們輸入路徑 http://localhost:8005/testCookies,然后查看發(fā)的請求。

可以看到下面那張圖是我們首次訪問服務器時發(fā)送的請求,可以看到服務器返回的響應中有 Set-Cookie 字段。
而里面的 key=value 值正是我們服務器中設置的值:

接下來我們再次刷新這個頁面可以看到在請求體中已經(jīng)設置了 Cookie 字段,并且將我們的值也帶過去了。

這樣服務器就能夠根據(jù) Cookie 中的值記住我們的信息了:

接下來我們換一個請求呢?是不是 Cookie 也會帶過去呢?接下來我們輸入路徑 http://localhost:8005 請求。

我們可以看到 Cookie 字段還是被帶過去了:

那么瀏覽器的 Cookie 是存放在哪呢?如果使用的是 Chrome 瀏覽器的話,那么可以按照下面步驟:
  • 在計算機打開 Chrome

  • 在右上角,一次點擊更多圖標→設置

  • 在底部,點擊高級

  • 在隱私設置和安全性下方,點擊網(wǎng)站設置

  • 依次點擊 Cookie→查看所有 Cookie 和網(wǎng)站數(shù)據(jù)


然后可以根據(jù)域名進行搜索所管理的 Cookie 數(shù)據(jù)。所以是瀏覽器替你管理了 Cookie 的數(shù)據(jù)。

如果此時你換成了 Firefox 等其他的瀏覽器,因為 Cookie 剛才是存儲在 Chrome 里面的,所以服務器又蒙圈了,不知道你是誰,就會給 Firefox 再次貼上小紙條。

Cookie 中的參數(shù)設置


說到這里,應該知道了 Cookie 就是服務器委托瀏覽器存儲在客戶端里的一些數(shù)據(jù),而這些數(shù)據(jù)通常都會記錄用戶的關鍵識別信息。

所以 Cookie 需要用一些其他的手段用來保護,防止外泄或者竊取,這些手段就是 Cookie 的屬性。

下面我就簡單演示一下這幾個參數(shù)的用法及現(xiàn)象:

①Path

設置為 cookie.setPath('/testCookies'),接下來我們訪問 http://localhost:8005/testCookies,可以看到在左邊和我們指定的路徑是一樣的。

所以 Cookie 才在請求頭中出現(xiàn),接下來我們訪問 http://localhost:8005,我們發(fā)現(xiàn)沒有 Cookie 字段了,這就是 Path 控制的路徑。

②Domain


設置為 cookie.setDomain('localhost'),接下來我們訪問 http://localhost:8005/testCookies。

我們發(fā)現(xiàn)下圖中左邊的是有 Cookie 的字段的,但是我們訪問 http://172.16.42.81:8005/testCookies,看下圖的右邊可以看到?jīng)]有 Cookie 的字段了。這就是 Domain 控制的域名發(fā)送 Cookie。

接下來的幾個參數(shù)就不一一演示了,相信到這里大家應該對 Cookie 有一些了解了。

Session


Cookie 是存儲在客戶端方,Session 是存儲在服務端方,客戶端只存儲 SessionId。
在上面我們了解了什么是 Cookie,既然瀏覽器已經(jīng)通過 Cookie 實現(xiàn)了有狀態(tài)這一需求,那么為什么又來了一個 Session 呢?
這里我們想象一下,如果將賬戶的一些信息都存入 Cookie 中的話,一旦信息被攔截,那么我們所有的賬戶信息都會丟失掉。

所以就出現(xiàn)了 Session,在一次會話中將重要信息保存在 Session 中,瀏覽器只記錄 SessionId,一個 SessionId 對應一次會話請求。

@RequestMapping('/testSession')
@ResponseBody
public String testSession(HttpSession session){
    session.setAttribute('testSession','this is my session');
    return 'testSession';
}


@RequestMapping('/testGetSession')
@ResponseBody
public String testGetSession(HttpSession session){
    Object testSession = session.getAttribute('testSession');
    return String.valueOf(testSession);
}

這里我們寫一個新的方法來測試 Session 是如何產(chǎn)生的,我們在請求參數(shù)中加上 HttpSession session。


然后在瀏覽器中輸入 http://localhost:8005/testSession 進行訪問可以看到在服務器的返回頭中在 Cookie 中生成了一個 SessionId。


然后瀏覽器記住此 SessionId 下次訪問時可以帶著此 Id,然后就能根據(jù)此 Id 找到存儲在服務端的信息了。

此時我們訪問路徑 http://localhost:8005/testGetSession,發(fā)現(xiàn)得到了我們上面存儲在 Session 中的信息。

那么 Session 什么時候過期呢?
  • 客戶端:和 Cookie 過期一致,如果沒設置,默認是關了瀏覽器就沒了,即再打開瀏覽器的時候初次請求頭中是沒有 SessionId 了。

  • 服務端:服務端的過期是真的過期,即服務器端的 Session 存儲的數(shù)據(jù)結構多久不可用了,默認是 30 分鐘。

既然我們知道了 Session 是在服務端進行管理的,那么或許你們看到這有幾個疑問,Session 是在哪創(chuàng)建的?Session 是存儲在什么數(shù)據(jù)結構中?接下來帶領大家一起看一下 Session 是如何被管理的。
Session 的管理是在容器中被管理的,什么是容器呢?Tomcat、Jetty 等都是容器。
接下來我們拿最常用的 Tomcat 為例來看下 Tomcat 是如何管理 Session 的。

在 ManageBase 的 createSession 是用來創(chuàng)建 Session 的:

@Override
public Session createSession(String sessionId) {
    //首先判斷Session數(shù)量是不是到了最大值,最大Session數(shù)可以通過參數(shù)設置
    if ((maxActiveSessions >= 0) &&
            (getActiveSessions() >= maxActiveSessions)) {
        rejectedSessions++;
        throw new TooManyActiveSessionsException(
                sm.getString('managerBase.createSession.ise'),
                maxActiveSessions);
    }

 // 重用或者創(chuàng)建一個新的Session對象,請注意在Tomcat中就是StandardSession
    // 它是HttpSession的具體實現(xiàn)類,而HttpSession是Servlet規(guī)范中定義的接口
    Session session = createEmptySession();


// 初始化新Session的值
    session.setNew(true);
    session.setValid(true);
    session.setCreationTime(System.currentTimeMillis());
    // 設置Session過期時間是30分鐘
    session.setMaxInactiveInterval(getContext().getSessionTimeout() * 60);
    String id = sessionId;
    if (id == null) {
        id = generateSessionId();
    }
    session.setId(id);// 這里會將Session添加到ConcurrentHashMap中
    sessionCounter++;

//將創(chuàng)建時間添加到LinkedList中,并且把最先添加的時間移除
    //主要還是方便清理過期Session
    SessionTiming timing = new SessionTiming(session.getCreationTime(), 0);
    synchronized (sessionCreationTiming) {
        sessionCreationTiming.add(timing);
        sessionCreationTiming.poll();
    }
    return session
}
到此我們明白了 Session 是如何創(chuàng)建出來的,創(chuàng)建出來后 Session 會被保存到一個 ConcurrentHashMap 中。

可以看 StandardSession 類:

protected Map<string, session> www.jintianxuesha.com sessions = new ConcurrentHashMap<>();
到這里大家應該對 Session 有簡單的了解了。Session 是存儲在 Tomcat 的容器中,所以如果后端機器是多臺的話,因此多個機器間是無法共享 Session 的。
此時可以使用 Spring 提供的分布式 Session 的解決方案,是將 Session 放在了 Redis 中。

Token


Session 是將要驗證的信息存儲在服務端,并以 SessionId 和數(shù)據(jù)進行對應,SessionId 由客戶端存儲,在請求時將 SessionId 也帶過去,因此實現(xiàn)了狀態(tài)的對應。

而 Token 是在服務端將用戶信息經(jīng)過 Base64Url 編碼過后傳給客戶端,每次用戶請求的時候都會帶上這一段信息,因此服務端拿到此信息進行解密后就知道此用戶是誰了,這個方法叫做 JWT(Json Web Token)。

Token 相比較于 Session 的優(yōu)點在于,當后端系統(tǒng)有多臺時,由于是客戶端訪問時直接帶著數(shù)據(jù),因此無需做共享數(shù)據(jù)的操作。

Token 的優(yōu)點:

  • 簡潔:可以通過 URL,POST 參數(shù)或者是在 HTTP 頭參數(shù)發(fā)送,因為數(shù)據(jù)量小,傳輸速度也很快。

  • 自包含:由于串包含了用戶所需要的信息,避免了多次查詢數(shù)據(jù)庫。

  • 因為 Token 是以 Json 的形式保存在客戶端的,所以 JWT 是跨語言的。

  • 不需要在服務端保存會話信息,特別適用于分布式微服務。


JWT 的結構


實際的 JWT 大概長下面的這樣,它是一個很長的字符串,中間用.分割成三部分。

JWT 是由三部分組成的:

①Header

Header 是一個 Json 對象,描述 JWT 的元數(shù)據(jù),通常是下面這樣子的:

{
  'alg''HS256',
  'typ''JWT'
}
上面代碼中:
  • alg 屬性表示簽名的算法(algorithm),默認是  HMAC SHA256(寫成 HS256)。

  • type 屬性表示這個令牌(Token)的類型(type),JWT 令牌統(tǒng)一寫為 JWT。最后,將上面的 Json 對象使用 Base64URL 算法轉成字符串。

JWT 作為一個令牌(Token),有些場合可能會放到 URL(比如 api.example.com/?token=xxx)。
Base64 有三個字符+、/和=,在 URL 里面有特殊含義,所以要被替換掉:=被省略、+替換成-,/替換成_ 。這就是 Base64URL 算法。

②Payload


Payload 部分也是一個 Json 對象,用來存放實際需要傳輸?shù)臄?shù)據(jù),JWT 官方規(guī)定了下面幾個官方的字段供選用:
  • iss (issuer):簽發(fā)人

  • exp (expiration time):過期時間

  • sub (subject):主題

  • aud (audience):受眾

  • nbf (Not Before):生效時間

  • iat (Issued At):簽發(fā)時間

  • jti (JWT ID):編號

當然除了官方提供的這幾個字段我們也能夠自己定義私有字段,下面就是一個例子:

{
  'name''xiaoMing',
  'age'14
}
默認情況下 JWT 是不加密的,任何人只要在網(wǎng)上進行 Base64 解碼就可以讀到信息,所以一般不要將秘密信息放在這個部分。這個 Json 對象也要用 Base64URL 算法轉成字符串。

③Signature


Signature 部分是對前面的兩部分的數(shù)據(jù)進行簽名,防止數(shù)據(jù)篡改。
首先需要定義一個秘鑰,這個秘鑰只有服務器才知道,不能泄露給用戶,然后使用 Header 中指定的簽名算法(默認情況是 HMAC SHA256)。
算出簽名以后將 Header、Payload、Signature 三部分拼成一個字符串,每個部分用.分割開來,就可以返給用戶了。
HS256 可以使用單個密鑰為給定的數(shù)據(jù)樣本創(chuàng)建簽名。當消息與簽名一起傳輸時,接收方可以使用相同的密鑰來驗證簽名是否與消息匹配。

Java 中如何使用 Token

上面我們介紹了關于 JWT 的一些概念,接下來如何使用呢?首先在項目中引入 Jar 包:

compile('io.jsonwebtoken:jjwt:0.9.0')

然后編碼如下:

// 簽名算法 ,將對token進行簽名 
SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256; 
// 通過秘鑰簽名JWT 
byte[] apiKeySecretBytes = DatatypeConverter.parseBase64Binary('SECRET'); 
Key signingKey = new SecretKeySpec(apiKeySecretBytes, signatureAlgorithm.getJcaName()); 
Map<String,Object> claimsMap = new HashMap<>(); 
claimsMap.put('name','xiaoMing'); 
claimsMap.put('age',14); 
JwtBuilder builderWithSercet = Jwts.builder()        
          .setSubject('subject')
          .setIssuer('issuer')
          .addClaims(claimsMap)
          .signWith(signatureAlgorithm, signingKey);
System.out.printf(builderWithSercet.compact());

發(fā)現(xiàn)輸出的 Token 如下:

eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJzdWJqZWN0IiwiaXNzIjoiaXNzdWVyIiwibmFtZSI6InhpYW9NaW5nIiwiYWdlIjoxNH0.3KOWQ-oYvBSzslW5vgB1D-JpCwS-HkWGyWdXCP5l3Ko

此時在網(wǎng)上隨便找個 Base64 解碼的網(wǎng)站就能將信息解碼出來:

總結


相信大家看到這應該對 Cookie、Session、Token 有一定的了解了。
接下來再回顧一下重要的知識點:
  • Cookie 是存儲在客戶端的。

  • Session 是存儲在服務端的,可以理解為一個狀態(tài)列表。擁有一個唯一會話標識 SessionId??梢愿鶕?jù) SessionId 在服務端查詢到存儲的信息。

  • Session 會引發(fā)一個問題,即后端多臺機器時 Session 共享的問題,解決方案可以使用 Spring 提供的框架。

  • Token 類似一個令牌,無狀態(tài)的,服務端所需的信息被 Base64 編碼后放到 Token 中,服務器可以直接解碼出其中的數(shù)據(jù)。


GitHub 代碼地址:

https://github.com/modouxiansheng/Doraemon

作者:不學無數(shù)的程序員

    本站是提供個人知識管理的網(wǎng)絡存儲空間,所有內容均由用戶發(fā)布,不代表本站觀點。請注意甄別內容中的聯(lián)系方式、誘導購買等信息,謹防詐騙。如發(fā)現(xiàn)有害或侵權內容,請點擊一鍵舉報。
    轉藏 分享 獻花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多