sau交流學習社區-songEagle開發系列:Vue + Vuex + Koa 中使用JWT(JSON Web Token)認證

1、前言

JWT(JSON Web Token),是爲了在網絡環境間傳遞聲明而執行的一種基於JSON的開放標準(RFC 7519)。

JWT不是一個新鮮的東西,網上相關的介紹已經很是多了。不是很瞭解的能夠在網上搜索一下相關信息。html

同步到sau交流學習社區:www.mwcxs.top/page/454.ht…前端


2、源碼

Talk is cheap. Show me the code.ios


3、工做流程

JWT本質來講是一個token。在先後端進行HTTP鏈接時來進行相應的驗證。git

1. 博客的後臺管理系統發起登陸請求,後端服務器校驗成功以後,生成JWT認證信息;github

2. 前端接收到JWT後進行存儲;web

3. 前端在以後每次接口調用發起HTTP請求時,會將JWT放到HTTP的headers參數裏的authorization中一塊兒發送給後端;json

4. 後端接收到請求時會根據JWT中的信息來校驗當前發起HTTP請求的用戶是不是具備訪問權限的,有訪問權限時則交給服務器繼續處理,沒有時則直接返回401錯誤。axios


4、實現過程

1. 登陸成功生成JWT

說明:如下代碼只保留了核心代碼,詳細代碼可在對應文件中查看,下同。
// /server/api/admin/admin.controller.js
const jwt = require('jsonwebtoken');
const config = require('../../config/config');

exports.login = async(ctx) => {
  // ...
  if (hashedPassword === hashPassword) {
    // ...
    // 用戶token
    const userToken = {
      name: userName,
      id: results[0].id
    };
    // 簽發token
    const token = jwt.sign(userToken, config.tokenSecret, { expiresIn: '2h' });
    // ...
  }
  // ...
}複製代碼

2. 添加中間件校驗JWT

// /server/middlreware/tokenError.js
const jwt = require('jsonwebtoken');
const config = require('../config/config');
const util = require('util');
const verify = util.promisify(jwt.verify);

/**
 * 判斷token是否可用
 */
module.exports = function () {
  return async function (ctx, next) {
    try {
      // 獲取jwt
      const token = ctx.header.authorization; 
      if (token) {
        try {
          // 解密payload,獲取用戶名和ID
          let payload = await verify(token.split(' ')[1], config.tokenSecret);
          ctx.user = {
            name: payload.name,
            id: payload.id
          };
        } catch (err) {
          console.log('token verify fail: ', err)
        }
      }
      await next();
    } catch (err) {
      if (err.status === 401) {
        ctx.status = 401;
        ctx.body = {
          success: 0,
          message: '認證失敗'
        };
      } else {
        err.status = 404;
        ctx.body = {
          success: 0,
          message: '404'
        };
      }
    }
  }
}複製代碼

3. Koa.js中添加JWT處理

此處在開發時須要過濾掉登陸接口(login),不然會致使JWT驗證永遠失敗。後端

// /server/config/koa.js
const jwt = require('koa-jwt');
const tokenError = require('../middlreware/tokenError');
// ...

const app = new Koa();

app.use(tokenError());
app.use(bodyParser());
app.use(koaJson());
app.use(resource(path.join(config.root, config.appPath)));

app.use(jwt({
  secret: config.tokenSecret
}).unless({
  path: [/^\/backapi\/admin\/login/, /^\/blogapi\//]
}));

module.exports = app;
複製代碼

4.前端處理

前端開發使用的是Vue.js,發送HTTP請求使用的是axios。

1. 登陸成功以後將JWT存儲到localStorage中(可根據我的須要存儲,我我的是比較喜歡存儲到localStorage中)。api

methods: {
       login: async function () {
         // ...
         let res = await api.login(this.userName, this.password);
         if (res.success === 1) {
           this.errMsg = '';
           localStorage.setItem('SONG_EAGLE_TOKEN', res.token);
           this.$router.push({ path: '/postlist' });
         } else {
           this.errMsg = res.message;
         }
       }
     }複製代碼


2. Vue.js的router(路由)跳轉前校驗JWT是否存在,不存在則跳轉到登陸頁面。

// /src/router/index.js
   router.beforeEach((to, from, next) => {
     if (to.meta.requireAuth) {
       const token = localStorage.getItem('SONG_EAGLE_TOKEN');
       if (token && token !== 'null') {
         next();
       } else {
         next('/login');
       }
     } else {
       next();
     }
   });複製代碼

3. axios攔截器中給HTTP統一添加Authorization信息

// /src/api/config.js
   axios.interceptors.request.use(
     config => {
       const token = localStorage.getItem('SONG_EAGLE_TOKEN');
       if (token) {
         // Bearer是JWT的認證頭部信息
         config.headers.common['Authorization'] = 'Bearer ' + token;
       }
       return config;
     },
     error => {
       return Promise.reject(error);
     }
   );複製代碼

4. axios攔截器在接收到HTTP返回時統一處理返回狀態

// /src/main.js
   axios.interceptors.response.use(
     response => {
       return response;
     },
     error => {
       if (error.response.status === 401) {
         Vue.prototype.$msgBox.showMsgBox({
           title: '錯誤提示',
           content: '您的登陸信息已失效,請從新登陸',
           isShowCancelBtn: false
         }).then((val) => {
           router.push('/login');
         }).catch(() => {
           console.log('cancel');
         });
       } else {
         Vue.prototype.$message.showMessage({
           type: 'error',
           content: '系統出現錯誤'
         });
       }
       return Promise.reject(error);
     }
   );複製代碼


5、總結

這個基本上就是JWT的流程。固然單純的JWT並非說絕對安全的,不過對於一個我的博客系統的認證來講仍是足夠的。

最後打個小廣告。目前正在開發新版的我的博客中,包括兩部分:

【前端】(github.com/saucxs/song…)

【後端】(github.com/saucxs/song…)

都已在GitHub上開源,目前在逐步完善功能中。歡迎感興趣的同窗fork和star。

相關文章
相關標籤/搜索