vue jwt 認證

JWT 認證

什麼是jwt?

JSON Web Token (JWT) 是目前最流行的跨域身份驗證解決方案
解決問題: session 不支持分佈式架構,無法支持橫向擴展,只能通過數據庫來保存會話數據實現共享。如果持久層失敗會出現認證失敗。
優點: 服務器不保存任何會話數據,即服務器變爲無狀態,使其更容易擴展。

JWT包含了使用 . 分割的三部分

  • Header 頭部

    {"alg":"HS256","typ":"JWT"}
    //"alg":"HS256"=>algorithm:HS256
    //"tpy":"JWT"=>type:JWT
    
  • Payload 載荷

iss (issuer):簽發人
exp (expiration time):過期時間
sub (subject):主題
aud (audience):受衆
nbf (Not Before):生效時間
iat (Issued At):簽發時間
jti (JWT ID):編號

  • Signature 簽名
    對前兩部分(Header,Payload)的簽名,防止數據篡改

    HMACSHA256(
     base64UrlEncode(header) + "." +
     base64UrlEncode(payload),
     secret)
    

    JWT作爲一個令牌(token),有些場合可能會放到URL(比如api.example.com/?token=xxx)。Base64有三個字符 +、/和=,在URL中有特殊的含義,所以要被替換掉:=被省略;+替換成-,/替換成 _ 這就是Base64URL算法。

使用方式
HTTP請求的頭信息Authorization字段裏面

Authorization: Bearer <token>

通過URL傳輸

http://www.xxx.com/pwa?token=xxxxx

POST請求放在請求體總也可

使用 vue-cli3 創建項目

vue create <project-name>

一、服務端返回簽發的 token

這裏服務端採用的是express,第三方模塊有 body-parser, jsonwebtoken

在用戶登錄和客戶端路由跳轉時分別進行token的發送和權限的校驗

//登錄時根據用戶名返回對應的token
app.post('/login',(req,res)=>{
   let {username} = req.body;
   if(username === 'admin'){ // 如果訪問的是admin 種植cookie
        res.json({
            code:0,
            username:'admin',//這裏的secret是密鑰,我這裏是定義的是一個字符串: let secret = 'siran'
            token:jwt.sign({username:'admin'},secret,{
                expiresIn:20  
            })
        })
   }else{
       res.json({
           code:1,
           data:'用戶名不存在'
       })
   }
});
//路由跳轉時根據token檢查權限
app.get('/validate',(req,res)=>{
    let token = req.headers.authorization;
    jwt.verify(token,secret,(err,decode)=>{ // 驗證token的可靠性
        if(err){
            return res.json({
                code:1,
                data:'token失效了'
            })
        }else{
            res.json({ 
                username:decode.username,
                code:0, // 給token續命
                token:jwt.sign({username:'admin'},secret,{
                    expiresIn:20  
                })
            })
        }
    });
});

二、路由配置

  • Home.vue 首頁
  • Profile.vue 個人中心
  • Login.vue 登錄頁面
export default new Router({
mode: 'history',
base: process.env.BASE_URL,
routes: [
  {
    path: '/',
    name: 'home',
    component: Home,
  },
  {
    path: '/profile',
    name: 'profile',
    component: Profile,
    meta: { needLogin: true }, // 路由跳轉時進行校驗,必須登錄了才能訪問當前路由組件
  },
  {
    path: '/login',
    name: 'login',
    component: Login,
  },
],
});

三、axios封裝

這裏封裝axios的目的主要有以下3點:

  • 爲每一個請求都設置攔截器,從而讓每一個請求都可以帶上 token
  • 之後在每一次請求時不用寫完整請求路徑,設置baseURL
  • 使整個請求響應的流程更符合Vuex的設計
import axios from 'axios';

class  AjaxRequest{
constructor() {
  this.baseURL = process.env.NODE_ENV === 'development' ? 'http://localhost:3000' : '/'; // 請求路徑 
  this.timeout = 3000; // 設置超時時間
}

setInterceptor(instance) { // 設置攔截器
  instance.interceptors.request.use(config => {
    config.headers.Authorization = `${localStorage.getItem('token')}`;
    return config; // 讓請求帶上token,前提是服務端允許請求頭帶上 Authorization字段
  }, (err) => {
    Promise.reject(err);
  });

  instance.interceptors.response.use(res => res.data, (err) => {
    Promise.reject(err);
  });
}

request(request) {
  const instance = axios.create();
  const config = {
    baseURL: this.baseURL,
    timeout: this.timeout,
    ...request,
  }; // 合併配置
  this.setInterceptor(instance);
  return instance(config);
}
}

export default new AjaxRequest();

四、測試接口

接口文件單獨寫在了 api 這個目錄下

import fetchData from '剛剛封裝的axios'

export const getTest = () => fetchData.request({ url: '/test' });
export const login = username => fetchData.request({
url: '/login',
method: 'POST',
data: {
  username,
},
});
export const validate = () => fetchData.request({ url: '/validate' });

五、在Vuex中發送請求

export default new Vuex.Store({
  state: {
    username: '',
  },
  mutations: {
    setUsername(state, username) {
      state.username = username;
    },
  },
  actions: {
    async login({ commit }, username) {
      const r = await login(username); // 登錄成功後返回用戶名信息
      if (r.token) { // 如果有返回token說明成功
        commit('setUsername', username); // 將用戶存入state中
        localStorage.setItem('token', r.token); // 將token存放起來
      } else { // 否則返回失敗的promise
        return Promise.reject(r);
      }
    },
  },
});

六、權限認證

在登錄成功之後給我們返回token後,用戶在通過路由跳轉不同的路由組件時需要對用戶的權限進行驗證,使用路由的全局守衛來針對每一個路由進行校驗

router.beforeEach(async (to, from, next) => {
  const isLogin = await store.dispatch('validate');
  if (isLogin) {
    // 如果已經登錄過了
    if (to.name === 'login') {//如果即將跳轉的路由還是登錄,則跳轉到profile
      next('/profile');
    } else {
      next();
    }
  } else { //如果沒有登錄過,則檢查要跳轉的路由需不需要登錄權限
    const flag = to.matched.some(item => item.meta.needLogin);
    if (flag) {
      next('/login');
    } else {
      next();
    }
  }
});

Vuex actions中驗證的方法

async validate({ commit }) {
    const r = await validate();//調用api中的接口
    if (r.code === 1) {
        return false;
    }
    commit('setUsername', r.username);
    localStorage.setItem('token', r.token); // 將token存放起來
    return true;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章