token驗證的實作方式很簡單,隻需要在所有後端接口中添加一個驗證是否存在token的中間件即可,接口的通路過程是調用→運作→傳回資料。是以我們在接口調用之後、運作之前的位置加一個驗證token的函數作為接口的中間件,進而驗證到非登入人員,将其頁面跳轉到登陸頁面即可。

1.在server端擷取admin端sessionStorage(或localStorage)存儲的token
我們要通過将sessionStorage傳入請求頭的方式在server端擷取頁面存儲的token。這樣做可以避免修改每一個接口(在每一個接口中擷取token,再傳到背景),背景可以直接在請求頭中擷取需要的token值。
方法是:在http.js中全局設定擷取token,将token值傳入請求頭**Request
Headers**中,然後背景接口中直接從請求頭中擷取token,進而實作驗證。
(1)使用axios裡的Interceptors方法
在npm官網查詢axiosadmin/http.js前端admin端将token傳入請求頭Request Header:
// 使用axios的interceptors攔截器,将http調用時攔截
http.interceptors.request.use(function (config) {
// 将token值傳入請求頭,"Bearer+空格"是行業規範,一看見Bearer(持票人,也就是被校驗者)就知道是token驗證
config.headers.Authorization = 'Bearer ' + sessionStorage.token
return config;
}, function (error) {
return Promise.reject(error);
});
(2)server後端接口處接收請求頭中的token值
server/routes/admin/index.js,添加中間件後的建立資料接口:
// 建立資料(增)
router.post('/', async(req, res, next) => {
// 擷取請求頭中的token
// 1.前端的authorization首字母大寫,後端小寫,自動對應
// 2.擷取到的有可能為空值
// 3.由于根據代碼規範傳入的token值帶有Bearer和空格,是以用split方法找到空格,在空格處分離Bearer和token值形成數組
// 4.再使用pop方法提取數組中最後一個值(也就是token值)
const token = String(req.headers.authorization || '').split(' ').pop()
// 使用jwt提取token資料(解密),當時我們傳入的是使用者id,是以解密出的就是使用者id和一個自帶參數
// 引入jsonwebtoken
const jwt = require('jsonwebtoken')
// 1.jwt的verify為解密方法
// 2.解密方法與加密方法相同,對token進行解密,需要用到全局定義的secret密鑰
// 3.利用解構指派{}方法将id解構
const { id } = jwt.verify(token, app.get('secret'))
// 利用id查找是否有此使用者id,以防僞造jwt登入
// 引入model模型
// const Admin = require('../../models/Admin')
// 查詢id,将查詢到的user放入req,友善其他接口也能夠使用
// req.user = await Admin.findById(id)
console.log(id)
// 執行下一步
await next()
}, async(req, res) => {
// const Model = require('../../model/' + req.params.resourse)
const model = await req.Model.create(req.body)
res.send(model)
})
建立一個分類,測試檢視終端查詢到的req.user:
沒問題,檢視Request Header:
3.http-assert 驗證提示工具
如果登陸異常狀态下無故跳轉到登入頁,使用者會以為網站有問題吧,是以我們要像登入接口一樣,發送錯誤狀态碼和錯誤資訊。
我們在登入接口中使用的發送錯誤資訊方式是:
// 如果使用者名為空
if(!username){
return res.status(422).send({
message: '請輸入使用者名'
})
}
有一個工具,可以用一行代碼将這一段替代,http-assert(用于測試函數、值是否存在、是否正确的工具包)。
使用方法:
cd server
npm i http-assert
安裝并引入:
// 如果使用者名為空
// if(!username){
// return res.status(422).send({
// message: '請輸入使用者名'
// })
// }
// assert方式修改後
assert(username, 422, '請輸入使用者名')
// 是否有username, 沒有就報錯422, 報錯資訊是‘’
測試:
報錯422沒問題,但是message沒有接收到。是因為http-assert是以抛出異常的方式進行報錯的,前端無法擷取到json資料。
是以我們要寫一個錯誤處理函數,對抛出的異常做處理,将異常中的内容進行捕獲并傳給前端admin。
// 錯誤處理函數(中間件)
app.use(async(err, req, res, next) => {
// 如果發送錯誤碼,将錯誤碼放到res輸出到前端顯示
// 如果擷取不到狀态碼,就是500錯誤,是以狀态碼報錯或500報錯
res.status(err.statusCode || 500).send({
message: err.message
})
})
在登入頁測試,沒問題:
可以将此類操作都改成這個格式,我這裡隻在token判斷中間件裡添加:
删除sessionStorage中的登入狀态token:
建立資料進行測試,出現了新問題:
意思是jwt格式錯誤,是jwt包傳遞過來的資訊,被我們接收并列印出來了。我們看一下Request Header:
應該是我們将token值删除後,jwt不認識undefined,不能将其定義為空,是以我們在前端admin的http.js攔截器函數中進行修改:
沒問題。
4.如果token驗證未登入,跳轉登入頁。
任何操作中,若驗證出現token報錯,直接跳轉到登入頁即可,我們可以使用錯誤處理函數,将所有token相關的報錯碼寫為401,當報錯401統一跳轉頁面到登入頁。
修改http.js響應攔截函數:
要想使用路由,該頁面上方要引入:
成功跳轉登入頁。
5.封裝token中間件
中間件内容很多,如果将每個需要用到這個中間件的接口都指派此段内容,代碼就會異常多且亂,是以我們将它封裝成一個函數,使用到時直接把函數名放入即可。
封裝:
使用,在除登入接口以外admin使用的接口都使用此中間件:
sessionStorage.clear()測試:
隻要使用到接口的地方,就會直接跳轉登入頁。
6.上傳圖檔的接口錯誤解決
當我們給圖檔上傳接口也添加token中間件後,突破就無法上傳了。
依然報錯401:
看一下network裡邊的接口調用情況,Request Header沒有帶上Authorization:
這是因為我們請求攔截過程中使用的是axios方法中的攔截方法,但是圖檔上傳我們用的是elementUI自帶的上傳請求庫進行圖檔的上傳。
是以修改圖檔上傳功能,SkillSet.vue、AdSet和富文本編輯器等使用到圖檔上傳功能頁面中圖檔上傳加一個傳遞header的方法:
(1)在全局添加token到header的方法,使用mixin()全局定義方法:
(2)使用全局定義的getAuthHeaders()函數方法:
在建立技能頁面測試:
沒問題,之後我們在所有圖檔上傳的元件中添加
:headers="getAuthHeaders()"
即可。
7.總結
隻要我們确立好實作token驗證的過程方向後就可以完成這個功能。登入的token驗證過程非常簡單,就是調用接口→發送token→接收token→判斷token→将token判斷結果傳給前端→如已登入運作接口(若未登入跳轉登入頁)→傳回資料,我們的搭建過程就是對每個步驟進行逐一尋找找方法、解決。
另外,在某些沒有使用接口的頁面上,比如建立廣告位功能,在儲存之前沒有任何接口操作,頁面就不會進行強制跳轉登入頁面的動作。是以我們需要做前端路由校驗,見下篇文章。