大致思路前端
後端書寫REST api時,有一些api是很是敏感的,好比獲取用戶我的信息,查看全部用戶列表,修改密碼等。若是不對這些api進行保護,那麼別人就能夠很容易地獲取並調用這些 api 進行操做。vue
因此對於一些api,在調用以前,咱們在服務端必須先對操做者進行「身份認證」,這就是所謂的鑑權。node
Json Web Token 簡稱爲 JWT,它定義了一種通訊雙方之間以 JSON 對象的形式安全傳遞信息的方法。JWT 能夠使用 HMAC 算法或者是 RSA 的公鑰密鑰對進行簽名,複雜度較高,換來的是更可靠的安全係數。ios
整個認證的流程大致以下:web
首先用戶登陸的接口是不用token認證的,由於這個接口自己就是token的產生來源。前端輸入用戶名和密碼後請求服務器登陸接口,服務器驗證用戶名密碼正確後,生成token並返回給前端,前端存儲token,並在後面的請求中把token帶在請求頭中傳給服務器,服務器驗證token有效,才能夠進行下一步操做。算法
服務器生成tokenjson
因爲咱們的服務端使用 Koa2 框架進行開發,除了要使用到 jsonwebtoken 庫以外,還要使用一個 koa-jwt 中間件,該中間件針對 Koa 對 jsonwebtoken 進行了封裝,使用起來更加方便。axios
const router = require('koa-router')(); const jwt = require('jsonwebtoken'); const userModel = require('../models/userModel.js');
router.post('/login', async (ctx) => { const data = ctx.request.body;const result = await userModel.findOne({ name: data.name, password: data.password }) if(result !== null){ const token = jwt.sign({ name: result.name, _id: result._id }, 'zhangnan', { expiresIn: '2h' }); return ctx.body = { code: 200, token: token, msg: '登陸成功' } }else{ return ctx.body = { code: 400, token: null, msg: '用戶名或密碼錯誤' } } }); module.exports = router;
(注意:這裏暫時不討論加鹽加密校驗,實際項目中密碼不可能這樣明文驗證,這裏只是爲了着重討論token鑑權。在驗證了用戶名密碼正確以後,就能夠調用 jsonwebtoken 的 sign() 方法來生成token,接收三個參數,第一個是載荷,用於編碼後存儲在 token 中的數據,也是驗證 token 後能夠拿到的數據;第二個是密鑰,本身定義的,隨便寫個什麼單詞均可以,可是驗證的時候必定要相同的密鑰才能解碼;第三個是options,能夠設置 token 的過時時間。)後端
前端獲取tokenapi
接下來就是前端獲取 token,這裏是在 vue.js 中使用 axios 進行請求,請求成功以後拿到 token 保存到 localStorage 中。
submit(){ axios.post('/login', { name: this.username, password: this.password }).then(res => { if(res.code === 200){ localStorage.setItem('token', res.data.token); }else{ this.$message('登陸失敗') } }) }
而後前端在請求後端api時,就把 token 帶在請求頭中傳給服務器進行驗證。每次請求都要獲取 localStorage 中的 token,這樣很麻煩,這裏使用了 axios 的請求攔截器,進行全局設置,對每次請求都進行了取 token 放到 headers 中的操做。
axios.interceptors.request.use(config => { const token = localStorage.getItem('token'); config.headers.common['Authorization'] = 'Bearer ' + token; return config; })
(這段代碼,若是是vue項目,能夠直接放在main.js中設置,表示每次請求前都會往請求頭的authorization裏塞一個token,至於那個Bearer 是koa-jwt的一個標識單詞,方便解析)
接下來服務器收到前端發過來的token後,就能夠進行驗證。
const koa = require('koa'); const koajwt = require('koa-jwt'); const app = new koa(); app.use(koajwt({ secret: 'zhangnan' }).unless({ path: [/\/register/, /\/login/] }));
(在這裏沒有定義錯誤處理函數,因爲出現錯誤後會返回401,因此我直接就讓前端來處理這種異常狀況,給出一個錯誤的交互提示便可)
分析koa-jwt源碼
咱們在node_mudules裏面找到koa-jwt/lib/resolvers文件夾下的auth-header.js文件,看下koa-jwt作了些什麼
(能夠看到它是先判斷請求頭中是否帶了 authorization,若是有,則經過正則將 token 從 authorization 中分離出來,這裏咱們也看到了Bearer這個單詞。若是沒有 authorization,則表明了客戶端沒有傳 token 到服務器,這時候就拋出 401 錯誤狀態。)
再看看上一級的vertify.js。
(能夠看到在 verify.js 中,它就是調用 jsonwebtoken 原生提供的 verify() 方法進行驗證返回結果。jsonwebtoken 的 sign() 方法用於生成 token ,而 verify() 方法固然則是用來解析 token。屬於jwt配對生產的兩個方法,因此koa-jwt這個中間件也沒作什麼事,無非就是用正則解析請求頭,調用jwt的vertify方法驗證token,在koa-jwt文件夾的index.js中,koa-jwt還調用koa-unless進行路由權限分發)
以上就是json web token的大致流程。