1、需求說明
在前後端分離開發中,前端使用Vue.js架構,通過axios發送異步HTTP請求,服務端就無法使用session的方式儲存使用者的登入資訊,因為用戶端的每一次異步請求在服務端都會被認為是一個新的session。我們可以使用jwt(jsonwebtoken)的方式實作使用者的校驗。
2、安裝相關依賴
服務端:
本文中使用Express作為服務端架構,需要在服務端安裝以下JWT依賴:
cnpm i jsonwebtoken --save
cnpm i express-jwt --save
前端:
在前端使用Vue.js架構,需要安裝axios用于發送HTTP請求:
cnpm i axios --save
3、代碼實作
3.1、服務端代碼
app.js入口檔案引入
express-jwt
,示例代碼如下:
var express = require('express');
var expressJwt = require('express-jwt');
var app = express();
app.use(expressJwt({
credentialsRequired:false,
secret: 'helloworld', //密鑰
algorithms: ['HS256'] //沒有此配置項,在jwt6.0.0版本會報錯:algorithms should be set
}).unless({
path: ['/login'] //設定不需要token驗證的路由
}))
express-jwt
會自動驗證請求頭中的
token
資訊。
在 routes/index.js 路由檔案中建立用于登入和使用者查詢的路由,示例代碼如下:
var express = require('express');
var router = express.Router();
var jwt = require('jsonwebtoken');
//使用者登入的路由,此路由不會進行token校驗
router.post('/login', function(req, res, next) {
let {username,pwd} = req.body
//模拟資料庫查詢賬号密碼
if(username === 'admin' && pwd === '123456'){
//生成Token資訊jwt.sign(payload, secretOrPrivateKey, [options, callback])
//payload參數為儲存到用戶端的使用者資訊,
//secretOrPrivateKey 為密鑰,要和 app.js 檔案中的 secret 的值保持一緻
//option為配置項,expiresIn是token的有效時長,機關為秒,值也可以為字元串,例如 '2d' 表示2天
let Token = jwt.sign({name: username,role: 1},'helloworld',{expiresIn: 60})
res.json({
code: 200,
token: 'Bearer '+Token //向用戶端響應的token前面必須添加 ’Bearer ’ 字首
})
}else{
res.json({
code: 500
})
}
});
//查詢使用者資訊的路由,此路由會校驗token
router.get('/api/user/find', function(req,res,next){
res.json({
code: 200,
result: ['tom','jack','lily']
})
})
module.exports = router;
jwt.sign()
方法也可以使用異步加密的方式,示例代碼如下:
//使用異步的方式生成token
jwt.sign(
{name:username,role:1},
'helloworld',
{expiresIn:60},
function(err,token){
console.log(token)
}
)
3.2、前端代碼
views/Login.vue 使用者登入元件,示例代碼如下:
<template>
<div>
<input type="text" v-model="username" placeholder="使用者名"></input>
<input type="password" v-model="pwd" placeholder="密碼"></input>
<button @click="login">登入</button>
</div>
</template>
<script>
import axiost from 'axios'
export default {
data(){
return {
username: '',
pwd: ''
}
},
methods: {
login(){ //登入按鈕點選事件
axios.post("/login",{
username: this.username,
pwd: this.pwd
}).then(res=>{
if(res.data.code === 200){
//儲存token資訊
localStorage.token = res.data.token
}
})
}
}
}
</script>
前端校驗token的方法有以下幾種:
(1)使用Vue的路由守衛校驗
// 在路由全局前置守衛中判斷 token 是否存在
router.beforeEach((to , from, next) => {
// 擷取 token
if (localStorage.token)) {
if (to.name === 'login') { // 如果使用者在login頁面
next('/');
} else {
next();
}
} else {
router.push('/login')
}
});
(2)在axios請求攔截器中添加token資訊
// axios請求攔截器
axios.interceptors.request.use(
config => {
if (localStorage.token) { // 判斷是否存在token
config.headers.authorization = localStorage.token;
}
return config;
},
err => {
return Promise.reject(err);
});
//axios響應攔截器
axios.interceptors.response.use(res => {
return res;
}, err=> {
if (err.response.status === 401) { //token校驗失敗,沒有通路權限
//輸出授權失敗錯誤資訊
} else {
//輸出其他錯誤資訊
}
return Promise.reject(err);
}
);
也可以直接在
axios
執行個體函數中設定
headers
,示例代碼如下:
let instance = axios.create({
baseURL: 'http://localhost:3000',
headers:{
authorization: localStorage.getItem('token')
}
})
instance.get('/xxx')
3.3、服務端校驗token并擷取使用者資訊
在服務端可以使用
express-jwt
自動完成校驗,也可以手動完成校驗,我們還可以在服務端擷取儲存到用戶端的使用者資訊。在服務端的
routes/index.js
路由檔案中編寫一個用于token驗證的測試路由,示例代碼如下:
var express = require('express');
var router = express.Router();
var jwt = require('jsonwebtoken');
//用于驗證token并擷取使用者資訊
router.get('/yz', function(req,res,next){
//從請求頭中擷取token内容
let token = req.headers.authorization
//由于token中包含 'Bearer '字首,需要把字首去掉獲得token值
token = token.replace('Bearer ','')
let result = null;
try{
//jwt.verify(token,secretOrPublicKey,[options,callback]) 驗證token的合法性
// secretOrPublicKey 參數為密鑰,要和生成token的密鑰保持一緻
result = jwt.verify(token,'helloworld')
console.log(result)
}catch(err){
console.error(err)
}
res.json({
result
})
})
module.exports = router;
在控制台列印的結果為如下圖所示:
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsIyZuBnLwkzNzIDNxMjM3IDNwEjMwIzLc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.png)
jwt.verify()
方法傳回的值為
jwt.sign()
方法中的
payload
對象參數,JWT 規定了7個官方字段,供選用,分别是:
- iss (issuer):簽發人
- exp (expiration time):過期時間
- sub (subject):主題
- aud (audience):閱聽人
- nbf (Not Before):生效時間
- iat (Issued At):簽發時間
- jti (JWT ID):編号