koa+jwt實現token驗證與刷新

JWT

JSON Web Token (JWT)是一個開放標準(RFC 7519),它定義了一種緊湊的、自包含的方式,用於做爲JSON對象在各方之間安全地傳輸信息。該信息能夠被驗證和信任,由於它是數字簽名的。javascript

本文只講Koa2 + jwt的使用,不瞭解JWT的話請到這裏)進行了解。前端

koa環境

要使用koa2+jwt須要先有個koa的空環境,搭環境比較麻煩,我直接使用koa起手式,這是我使用koa+typescript搭建的空環境,若是你也常常用koa寫寫小demo,能夠點個star,方便~java

安裝koa-jwt

koa-jwt主要做用是控制哪些路由須要jwt驗證,哪些接口不須要驗證:ios

import  *  as  koaJwt  from  'koa-jwt';

//路由權限控制 除了path裏的路徑不須要驗證token 其餘都要
app.use(
    koaJwt({
        secret:  secret.sign
    }).unless({
        path: [/^\/login/, /^\/register/]
    })
);

上面代碼中,除了登陸、註冊接口不須要jwt驗證,其餘請求都須要。git

使用jsonwebtoken生成、驗證token

執行npm install jsonwebtoken安裝jsonwebtoken
相關代碼:github

import  *  as  jwt  from  'jsonwebtoken';

const secret = 'my_app_secret';
const payload = {user_name:'Jack', id:3, email: '1234@gmail.com'};
const token = jwt.sign(payload, secret, { expiresIn:  '1h' });

上面代碼中經過jwt.sign來生成一個token,
參數意義:web

  • payload:載體,通常把用戶信息做爲載體來生成token
  • secret:祕鑰,能夠是字符串也能夠是文件
  • expiresIn:過時時間 1h表示一小時

在登陸中返回token

import * as crypto from 'crypto';
import * as jwt from 'jsonwebtoken';

async login(ctx){

  //從數據庫中查找對應用戶
  const user = await userRespository.findOne({
    where: {
      name: user.name
    }
  });

  //密碼加密
  const psdMd5 = crypto
    .createHash('md5')
    .update(user.password)
    .digest('hex');

  //比較密碼的md5值是否一致 若一致則生成token並返回給前端
  if (user.password === psdMd5) {
    //生成token
    token = jwt.sign(user, secret, { expiresIn:  '1h' });
    //響應到前端
    ctx.body = {
      token
    }
  }

}

前端攔截器

前端經過登陸拿到返回過來的token,能夠將它存在localStorage裏,而後再之後的請求中把token放在請求頭的Authorization裏帶給服務端。
這裏以axios請求爲例,在發送請求時,經過請求攔截器把token塞到header裏:typescript

//請求攔截器
axios.interceptors.request.use(function(config) {
    //從localStorage裏取出token
    const token = localStorage.getItem('tokenName');
    //把token塞入Authorization裏
    config.headers.Authorization = `Bearer ${token}`;
    
    return config;
  },
  function(error) {
    // Do something with request error
    return Promise.reject(error);
  }
);

服務端處理前端發送過來的Token

前端發送請求攜帶token,後端須要判斷如下幾點:數據庫

  1. token是否正確,不正確則返回錯誤
  2. token是否過時,過時則刷新token 或返回401表示須要重新登陸

關於上面兩點,須要在後端寫一箇中間件來完成:npm

app.use((ctx, next) => {
  if (ctx.header && ctx.header.authorization) {
    const parts = ctx.header.authorization.split(' ');
    if (parts.length === 2) {
      //取出token
      const scheme = parts[0];
      const token = parts[1];
      
      if (/^Bearer$/i.test(scheme)) {
        try {
          //jwt.verify方法驗證token是否有效
          jwt.verify(token, secret.sign, {
            complete: true
          });
        } catch (error) {
          //token過時 生成新的token
          const newToken = getToken(user);
          //將新token放入Authorization中返回給前端
          ctx.res.setHeader('Authorization', newToken);
        }
      }
    }
  }

  return next().catch(err => {
    if (err.status === 401) {
      ctx.status = 401;
      ctx.body =
        'Protected resource, use Authorization header to get access\n';
    } else {
      throw err;
    }});
 });

上面中間件是須要驗證token時都須要走這裏,能夠理解爲攔截器,在這個攔截器中處理判斷token是否正確及是否過時,並做出相應處理。

後端刷新token 前端須要更新token

後端更換新token後,前端也須要獲取新token 這樣請求才不會報錯。
因爲後端更新的token是在響應頭裏,因此前端須要在響應攔截器中獲取新token。
依然以axios爲例:

//響應攔截器
axios.interceptors.response.use(function(response) {
    //獲取更新的token
    const { authorization } = response.headers;
    //若是token存在則存在localStorage
    authorization && localStorage.setItem('tokenName', authorization);
    return response;
  },
  function(error) {
    if (error.response) {
      const { status } = error.response;
      //若是401或405則到登陸頁
      if (status == 401 || status == 405) {
        history.push('/login');
      }
    }
    return Promise.reject(error);
  }
);
相關文章
相關標籤/搜索