cookie✘session✘jwt

cookie✘session✘jwt

寫在前面

PS:已經有不少文章寫過這些東西了,我寫的目的是爲了本身的學習。所學只是爲了更好地瞭解用戶登陸鑑權問題。

咱們都知道HTTP是一個無狀態的協議javascript

什麼是無狀態?html

用http協議進行兩臺計算機交互時,不管是服務器仍是瀏覽器端,http協議只負責規定傳輸格式,你怎麼傳輸,我怎麼接受怎麼返回。它並無記錄你上次訪問的內容,你上次傳遞的參數是什麼,它無論的。前端

回到咱們要解決的問題,就是用戶登陸到了一個網站(index.html),而後點擊主頁上的某一個超連接跳轉到其餘頁面(another.html),這個時候你在another.html頁面就沒有了登錄狀態。這樣意味着咱們每次跳轉一個頁面都要進行一次登錄操做,這是極其不合理的。java

爲了保證登陸信息以及狀態信息可以傳遞下去,就引入了其餘機制git

session和cookie

Cookie

(1)cookie是實際存在的,存在於客戶端,用戶可見可修改,不安全。github

(2)cookie在一個域名下是全局的,只要設置path爲/,便可從該域名下的任意頁面讀取cookie中的信息。web

(3)爲了安全,HttpOnly設置爲true,這樣能夠必定程度上的預防XSS(跨站腳本攻擊)redis

(4)瀏覽器禁用cookie以後,這種狀況下會使用url重寫的技術來進行會話跟蹤,即在url後面加上sid=xxx參數。算法

大多數應用都是基於cookie來實現session跟蹤的。mongodb

Session

session是一種機制,並不實際存在,它由服務器負責管理。關閉瀏覽器以後,session就會丟失。

具體的過程以下:

(1)客戶端第一次發送請求到服務器,服務器生成一個惟一的sessionId,一個sessionId對應一個用戶,該sessionId能夠存放在Redis或者mongodb中,具體存放在哪裏看我的選擇

(2)後端將該sessionId放到響應頭的Set-Cookie字段,返給前端。

響應頭

(3)前端記下該sessionId並放到cookie字段,以後每次客戶端請求服務器時都會在請求頭帶上cookie字段,服務端根據sessionId來獲取具體信息(好比TTL,過時時間,用戶id)

image

Koa2中使用session

首先固然是引入包了啊,這裏選擇了koa-session2`。koa-session2`已經幫你作好了全部,使用起來至關簡單,方便快速開發。

const Koa = require("koa")
const app = new Koa()
const session = require("koa-session2")

app.use(session({
    stort: new RedisStore(), //存放session的地方,我這裏選擇放到redis裏
    key: "SESSION_ID"
}))

new RedisStore()又是什麼呢?其實查看koa-session2的git倉https://github.com/Secbone/koa-session2就能知道

如下來自官方git倉:

const Redis = require("ioredis");
const { Store } = require("koa-session2");

class RedisStore extends Store {
    constructor() {
        super();
        this.redis = new Redis(); // 鏈接redis
    }

    async get(sid, ctx) { 
        let data = await this.redis.get(`SESSION:${sid}`);
        return JSON.parse(data);
    }

    async set(session, { sid =  this.getID(24), maxAge = 1000000 } = {}, ctx) {
        try {
            // Use redis set EX to automatically drop expired sessions
            // 設置redis的Ex 以自動丟棄過時的session
            await this.redis.set(`SESSION:${sid}`, JSON.stringify(session), 'EX', maxAge / 1000);
        } catch (e) {}
        return sid;
    }

    async destroy(sid, ctx) { // 刪除redis中的數據
        return await this.redis.del(`SESSION:${sid}`);
    }
}

module.exports = RedisStore;

接下來就是判斷登錄,以及將須要的信息寫入session

let sid = ctx.cookies.get("SESSION_ID") // 得到cookie中的sid

ctx.session.myinfo = {a: 1, b: 2} //將一個數據對象放到session中

// 最後的結果就是:

// redis中的鍵爲SESSION:sid

// 值爲{myinfo: {a: 1, b: 2}}

redis

當你退出時,清空cookie和session便可

ctx.cookies.set("SESSION_ID", "")
ctx.session = null

JWT

簡答認識JWT

讓咱們來看看重頭戲JWT,全稱JSONWebToken,是一種目前較爲流行的驗證方式。

JWT由三部分組成,第一部分咱們稱它爲頭部(header),

//header 
{
    'typ': 'JWT', // 類型
    'alg': 'HS256' // 加密算法
}
// 對其base64 獲得了第一個部分

第二部分咱們稱其爲載荷(payload, 相似於飛機上承載的物品)

// payload 存放有效信息的地方 好比userId
{
  "id": "1234567890",
  "name": "John Doe",
  "isMan": true
}
// 對其base64 獲得第二個部分

第三部分是簽證(signature).

// 將base64後的header和base64後的payload使用.鏈接組成新的字符串,而後使用header中聲明的加密方式對其進行加鹽secret組合加密,獲得了第三個部分

將三部分用.鏈接成一個完整的字符串,獲得了最終的jwt。

注意

  • 不該該在jwt的payload部分存放敏感信息,由於該部分是客戶端可解密的部分。
  • 保護好secret私鑰,該私鑰很是重要。
  • 若是能夠,請使用https協議

具體的過程以下:

(1)客戶端第一次發送請求到服務端,服務器驗證用戶信息

(2)服務端生成一個token發送給客戶端

token

(3)客戶端保存token,以後每次請求時帶上這個token

image

(4)服務端驗證token,返回數據。

與session的過程是相似的,可是缺乏了將jwt保存到服務端,這樣便於擴展,不會由於登陸到不一樣的服務器致使session沒法共享。

Koa2中使用jwt

第一步固然仍是下包。。npm install jsonwebtoken

const jwt = require("jsonwebtoken")

服務端生成token,並返給前端

// 生成token
const token = jwt.sign({role: user.role, id: user._id}, key, {expiresIn: "1 days"}) // 第一個參數爲負載的信息,第二個參數爲secret,第三個參數我是過時時間

// 返回前端
ctx.body = {
    token: token
}

客戶端以後發起請求,應該帶上token字段,將其放在authorization請求頭字段或者以query的方式。

if(ctx.header.authorization && ctx.headers.authorization.split(' ')[0] === "Bearer") {
        token = ctx.header.authorization.split(' ')[1]
    } else if(ctx.query && ctx.query.token) {
        token = ctx.query.token
    }

而後服務端驗證token,進行相應的處理返回數據。

// 解密token 通常使用jwt.verify不適用jwt.decode
let decoded = jwt.verify(token, key) // 獲得token中包含的信息對象
相關文章
相關標籤/搜索