Token到底是什么?!

隨著Web應(yīng)用的發(fā)展,為了保證API通信的安全性,很多項目在進(jìn)行設(shè)計時會采用JSON Web TokenJWT)的解決方案。

JWT是一種開放標(biāo)準(zhǔn)(RFC 7519),它定義了一種緊湊且自包含的方式,用于在各方之間安全地傳輸信息作為JSON對象。這種信息可以被驗證和信任,因為它是數(shù)字簽名的。

那么JWT中的Token到底是什么?接下來,我們將以登錄功能為例進(jìn)行Token的分析。

登錄流程

很多小伙伴對登錄的流程已經(jīng)很熟悉了,我們來看一個最基本的后臺系統(tǒng)的登錄流程

登錄流程圖.png

流程圖很清楚了,接下來我們使用 V2Koa 實現(xiàn)一個登錄過程,來看看Token到底是什么

Vue2 + Koa 實現(xiàn)登錄

前端代碼

1. 前端點擊事件

數(shù)據(jù)的校驗就忽略掉,感興趣的同學(xué)可自行書寫或者找我要源碼,直接看點擊事件

handleLogin() {
  this.$refs.loginForm.validate((valid) => {
    if (valid) {
      this.loading = true;
      // 這里使用了VueX
      this.$store
        .dispatch("user/login", this.loginForm)
        .then(() => {
        this.$router.push({ path: this.redirect || "/" });
        this.loading = false;
      })
        .catch(() => {
        this.loading = false;
      });
    } else {
      return false;
    }
  });
}

2. Vuex中的action

校驗通過后觸發(fā)VueXUser模塊的Login方法:

async login(context, userInfo) {
  const users = {
    username: userInfo.mobile,
    password: userInfo.password
  }
  const token = await login(users)
  // 在這里大家可以對返回的數(shù)據(jù)進(jìn)行更詳細(xì)的邏輯處理
  context.commit('SET_TOKEN', token)
  setToken(token)
}

3. 封裝的接口

export function login(data) {
  return request({
    url: '/login',
    method: 'post',
    data
  })
}

以上三步,是我們從前端向后端發(fā)送了請求并攜帶著用戶名和密碼,接下來,我們來看看Koa中是如何處理前端的請求的

Koa 處理請求

首先介紹一下Koa

Koa 基于Node.js平臺,由 Express 幕后的原班人馬打造,是一款新的服務(wù)端 web 框架

Koa的使用極其簡單,感興趣的小伙伴可以參考官方文檔嘗試用一下

Koa官網(wǎng):https://koa.bootcss.com/index.html#introduction

1. 技術(shù)說明

在當(dāng)前案例的koa中,使用到了jsonwebtoken的依賴包幫助我們?nèi)ゼ用苌珊徒饷?code>Token

2. 接口處理

const { login } = require("../app/controller/user")
const jwt = require("jsonwebtoken")
const SECRET = 'test_';
router.post('/login', async (ctx, next) => {
    const { username, password } = ctx.request.body
    // 這里是調(diào)用Controller中的login方法來跟數(shù)據(jù)庫中的數(shù)據(jù)作對比,可忽略
    const userList = await login(username, password)
    
    if (!userList) {
        // 這里的errorModel是自己封裝的處理錯誤的模塊
        ctx.body = new errorModel('用戶名或密碼錯誤', '1001')
        return
    }
  
    // ↓↓↓↓↓↓↓↓↓↓↓↓↓↓ ※ 重點看這里 ※ ↓↓↓↓↓↓↓↓↓↓↓↓↓↓
    const token = jwt.sign({ userList }, SECRET, { expiresIn: "1h" })
    
    ctx.body = {
        success: true,
        state: 200,
        message: 'login success',
        data: token
    };
    return;
})

關(guān)于 JWT

上面的重點代碼大家看到了,接下來具體給大家解釋下JWT

Jwt由三部分組成:headerpayload、signature

export interface Jwt {
    header: JwtHeader;
    payload: JwtPayload | string;
    signature: string;
}

header頭部

里面的包含的內(nèi)容有很多,比如用于指定加密算法的alg、指定加密類型的typ,全部參數(shù)如下所示:

export interface JwtHeader {
    alg: string | Algorithm;
    typ?: string | undefined;
    cty?: string | undefined;
    crit?: Array<string | Exclude<keyof JwtHeader, 'crit'>> | undefined;
    kid?: string | undefined;
    jku?: string | undefined;
    x5u?: string | string[] | undefined;
    'x5t#S256'?: string | undefined;
    x5t?: string | undefined;
    x5c?: string | string[] | undefined;
}

payload負(fù)載

payload使我們存放信息的地方,里面包含了簽發(fā)者過期時間、簽發(fā)時間等信息

export interface JwtPayload {
    [key: string]: any;
    iss?: string | undefined;
    sub?: string | undefined;
    aud?: string | string[] | undefined;
    exp?: number | undefined;
    nbf?: number | undefined;
    iat?: number | undefined;
    jti?: string | undefined;
}

signature簽名

signature需要使用編碼后的headerpayload以及我們提供的一個密鑰(SECRET),然后使用header 中指定的簽名算法進(jìn)行簽名

關(guān)于 jwt.sign()

jwt.sign()方法,需要三個基本參數(shù)和一個可選參數(shù):payload、secretOrPrivateKey、options和一個callback

export function sign(
    payload: string | Buffer | object,
    secretOrPrivateKey: Secret,
    options: SignOptions,
    callback: SignCallback,
): void;

payload是我們需要加密的一些信息,這個參數(shù)對應(yīng)上面koa代碼中的{ userList },而userList則是我從數(shù)據(jù)庫中查詢得到的數(shù)據(jù)結(jié)果

secretOrPrivateKey則是我們自己定義的秘鑰,用來后續(xù)驗證Token時所用

options選項中有很多內(nèi)容,例如加密算法algorithm、有效期expiresIn等等

export interface SignOptions {
    /**
     * Signature algorithm. Could be one of these values :
     * - HS256:    HMAC using SHA-256 hash algorithm (default)
     * - HS384:    HMAC using SHA-384 hash algorithm
     * - HS512:    HMAC using SHA-512 hash algorithm
     * - RS256:    RSASSA using SHA-256 hash algorithm
     * - RS384:    RSASSA using SHA-384 hash algorithm
     * - RS512:    RSASSA using SHA-512 hash algorithm
     * - ES256:    ECDSA using P-256 curve and SHA-256 hash algorithm
     * - ES384:    ECDSA using P-384 curve and SHA-384 hash algorithm
     * - ES512:    ECDSA using P-521 curve and SHA-512 hash algorithm
     * - none:     No digital signature or MAC value included
     */
    algorithm?: Algorithm | undefined;
    keyid?: string | undefined;
    /** expressed in seconds or a string describing a time span [zeit/ms](https://github.com/zeit/ms.js).  Eg: 60, "2 days", "10h", "7d" */
    expiresIn?: string | number | undefined;
    /** expressed in seconds or a string describing a time span [zeit/ms](https://github.com/zeit/ms.js).  Eg: 60, "2 days", "10h", "7d" */
    notBefore?: string | number | undefined;
    audience?: string | string[] | undefined;
    subject?: string | undefined;
    issuer?: string | undefined;
    jwtid?: string | undefined;
    mutatePayload?: boolean | undefined;
    noTimestamp?: boolean | undefined;
    header?: JwtHeader | undefined;
    encoding?: string | undefined;
    allowInsecureKeySizes?: boolean | undefined;
    allowInvalidAsymmetricKeyTypes?: boolean | undefined;
}

callback則是一個回調(diào)函數(shù),有兩個參數(shù),默認(rèn)返回Token

export type SignCallback = (
    error: Error | null,
    encoded: string | undefined,
) => void;

通過以上方法加密之后的結(jié)果就是一個Token

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjU3ZmVmMTY0ZTU0YWY2NGZmYzUzZGJkNSIsInhzcmYiOiI0ZWE1YzUwOGE2NTY2ZTc2MjQwNTQzZjhmZWIwNmZkNDU3Nzc3YmUzOTU0OWM0MDE2NDM2YWZkYTY1ZDIzMzBlIiwiaWF0IjoxNDc2NDI3OTMzfQ.PA3QjeyZSUh7H0GfE0vJaKW4LjKJuC3dVLQiY4hii8s

總結(jié)

在整個的Koa中,用到了jsonwebtoken這個依賴包,里面有sign()方法

而我們前端所得到的數(shù)據(jù)通過sign()加密出來的包含自定義秘鑰的一份用戶信息而已

至于用戶信息中有什么內(nèi)容,可以隨便處理,比如用戶的ID、用戶名、昵稱、頭像等等

那么這個Token后續(xù)有什么用呢?

后續(xù)我們可以在前端的攔截器中配置這個Token,讓每一次的請求都攜帶這個Token,因為Koa后續(xù)需要對每一次請求進(jìn)行Token的驗證

比如登錄成功后請求用戶的信息,獲取動態(tài)路由,再通過前端的router.addRoutes()將動態(tài)路由添加到路由對象中去即可

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容