###### tags: `Vue` @N9_MHei9TA2FRZJGWDx4cg # Vue+JWT - [JWT是什麼](#JWT是什麼) - [流程](#流程) - [如何透過Vue實作JWT](#如何透過Vue實作JWT) - [程式](#程式) - [建立專案並新增頁面](#1-建立專案並新增頁面) - [帳號密碼驗證流程](#2-帳號密碼驗證流程) - [Vue-Router-跳轉時攔截](#3-Vue-Router-跳轉時攔截) ## JWT是什麼 JWT全名為JSON Web Token,詳細部分可以參考[這篇文章(JWT — 原理介紹)](https://medium.com/%E4%BC%81%E9%B5%9D%E4%B9%9F%E6%87%82%E7%A8%8B%E5%BC%8F%E8%A8%AD%E8%A8%88/jwt-json-web-token-%E5%8E%9F%E7%90%86%E4%BB%8B%E7%B4%B9-74abfafad7ba)。 在實作上,其實只要注意不要再jwt裡放敏感的資料,如密碼之類的。因為jwt實際上由3個部分組成,Header、Payload、Signature,而資料都會放在第二部分的Paylaod,但是Header與Payload只會通過Base64進行加密,所以較敏感的資料不適合放在JWT理。 ## 流程 1. 伺服器發送JWT 2. 客戶端接收並儲存JWT 3. 每次跳轉時檢查JWT並延長JWT的時間 4. 若JWT不合法則轉回登入頁面 ## 如何透過Vue+JWT - jwt就交給後端做發送和驗證的部分,如此一來jwt所需的密鑰就只會存在後端,減少密鑰外洩的疑慮。 - 前端Vue就透過呼叫<a href="/0UX34j76S_iRuOaKnzR7dw">後端</a>後端去取得jwt。 - 取得jwt後,存在localStorage裡 - 每次Vue Vouter轉到新頁面前都先呼叫後端來檢查jwt是否合法。如果合法就延長jwt的時間,反之則轉到登入頁面。 ## 程式 ### 1. 建立專案並新增頁面 1. 建立vue-cli專案,詳細說明見[這篇文章](https://hackmd.io/CuJJdABgQBC6AaPHi5BVEQ),使用自訂設定,加入Vue Router。此時目錄應長這樣。 <img src="https://i.imgur.com/GBfo9F1.png" width="200px"/> 2. 建立登入頁面loginView.vue到View資料夾下,LoginView的程式碼如下。 ```htmlembedded= <template> <div> <form> <label> Account: <input v-model="account"/> </label> <br/> <label> Password: <input v-model="password" type="password"/> </label> <br/> <button>Login</button> </form> </div> </template> <script> export default { data() { return { account: "", password: "" } }, methods: { submit(){ //這邊執行登入 } } } </script> ``` 3. 在router/index.js裡新增以下程式碼來新增頁面。 ```javascript= import { createRouter, createWebHistory } from 'vue-router' import HomeView from '../views/HomeView.vue' import LoginView from "../views/loginView.vue" //新增的 const routes = [ //......// { //新增的 path: '/login', //新增的 name: 'login', //新增的 component: LoginView //新增的 }, //新增的 ] const router = createRouter({ history: createWebHistory(process.env.BASE_URL), routes }) export default router ``` >新增完進入 http://localhost:8080/login 會看到以下畫面 <img src="https://i.imgur.com/xt0LkPe.png" /> - 為方便測試,可以在App.vue加入以下程式碼: ```htmlembedded= <template> <nav> <router-link to="/">Home</router-link> | <router-link to="/about">About</router-link> | <router-link to="/login">Login</router-link> <!--新增的--> </nav> <router-view/> </template> ``` ### 2. 帳號密碼驗證流程 1. View的部分處理完後,接下來就是處理帳號密碼發送到伺服器端做驗證,並回傳jwt給我們。 - 假設伺服器架在 http://localhost:65432 上,如懶得架可使用[nodejs](/13lRUwxJRCOK4QV6pPMcoQ)或是[java spring boot](/0UX34j76S_iRuOaKnzR7dw)。 - 在vue.config.js裡新增以下程式碼 ```javascript module.exports = defineConfig({ //... devServer: { //新增的 proxy: { //新增的 "/api": { //新增的 target: "http://localhost:34567", //新增的 pathRewrite: { '^/api': '' }, //新增的 } //新增的 } //新增的 } //新增的 //... }) ``` - 在此專案的根目錄新增資料夾/api 專門放api相關的檔案,並在其新增login.js。 <img src="https://i.imgur.com/qxcXoxK.png" width="200px"/> - 安裝axios,login.js會用到 ```shell npm install axios ``` - login.js的程式碼如下: ```javascript= import axios from "axios" async function login(userName, password){ const result= { status: 1, token: "" } const url = "/api/login" const data = { username: userName, password: password } const res = await axios.post(url, data) if(res.data.status){ result.status = 0 result.token = res.data.token setLoginStore({ isLogin: true, token: result.token, userName: res.data.username }) } return result } async function authToken(token){ const result = { status: "", token: "", userName: "" } const url = "/api/auth" const data = { token: token } const res = await axios.post(url, data) if(res.status){ result.status = "jwt verify success" result.token = res.data.token result.userName = res.data.username setLoginStore({ isLogin: true, token: result.token, userName: result.userName }) }else{ setLoginStore({ isLogin: false, token: "", userName: "" }) } return result } function setLoginStore(options){ if(options.isLogin){ window.localStorage.setItem("isLogin", options.isLogin) } if(options.token){ window.localStorage.setItem("token", options.token) } if(options.userName){ window.localStorage.setItem("username", options.username) } } export {login, authToken} ``` - 其中login()與auth()都會呼叫setLoginStore()來將token存在localStorage - 在/view/loginView.vue新增以下程式碼: ```htmlembedded= <template> <div> <form @submit.prevent="submit()"> //修改 //... </form> </div> </template> <script> import { login } from "@/api/login" //新增 import router from "@/router" //新增 export default { //... methods: { async submit(){ //新增 let res = await login(this.account, this.password) //新增 if(res.status==0){ //新增 router.push({name:"home"}) //新增 } //新增 } //新增 } } </script> ``` - 如此一來,登入部分就完成了。接下來就只剩下在每次跳轉前驗證token。 <img src="https://i.imgur.com/tbdZirK.gif" /> ### 3. Vue Router 跳轉時攔截 1. 要實作這功能,主要用到的是Vue Router的 beforeResolve。詳細功能請見[BeforeResolve](/ArKsDjFuRVOVNQzrz1QQIg#312-beforeResolve) - 簡單講,BeforeResolve裡的部分會在每次跳轉時執行,那我們只要在其中加入驗證token的部分就可以實作了。 1. 首先,在/router/index.js裡的routes,在想要登入才可進入的頁面中加入以下程式碼,這裡是用Home Page來示範: ```javascript= const routes = [ { path: '/', name: 'home', component: HomeView, meta: { //新增 needLogin: true //新增 } //新增 } //... ] ``` - route的meta屬性是可以自訂的,在這裡使用needLogin的布林值來標記此頁面要登入。 2. 然後,在/router/index.js裡,createRouter之後加入以下程式碼: ```javascript= import { authToken } from '@/api/login' router.beforeResolve( async to=>{ if(to.meta.needLogin){ const isLogin = window.localStorage.getItem("isLogin") if(!isLogin) return {name: "login"} const token = window.localStorage.getItem("token") const authResult = await authToken(token) if(!authResult.status) return {name: "login"} } }) ``` - 在每次跳轉時,檢查要去的頁面的meta屬性是否有needLogin且為true,有就表示此頁面需要登入。 - 若需要登入則去localStorage裡取相對應的資料。 - 成功的話,在未登入時欲進入HomePage應會轉至login頁面: <img src="https://i.imgur.com/Bt6ozpX.gif" /> 2. 如此一來簡單的jwt功能就做完了,因為資料是存在localStorage裡所以就算重新整理也還是可以驗證。 ## 檔案下載 [完整程式碼](https://drive.google.com/file/d/1hWxvzF4NFcfZaH0F5SBGxUv_Jyf9Hf2g/view?usp=share_link) ## 參考文獻 [JSON Web Token](https://jwt.io/) [透過 JWT 實作驗證機制](https://medium.com/%E9%BA%A5%E5%85%8B%E7%9A%84%E5%8D%8A%E8%B7%AF%E5%87%BA%E5%AE%B6%E7%AD%86%E8%A8%98/%E7%AD%86%E8%A8%98-%E9%80%8F%E9%81%8E-jwt-%E5%AF%A6%E4%BD%9C%E9%A9%97%E8%AD%89%E6%A9%9F%E5%88%B6-2e64d72594f8) [JWT(JSON Web Token) — 原理介紹](https://medium.com/%E4%BC%81%E9%B5%9D%E4%B9%9F%E6%87%82%E7%A8%8B%E5%BC%8F%E8%A8%AD%E8%A8%88/jwt-json-web-token-%E5%8E%9F%E7%90%86%E4%BB%8B%E7%B4%B9-74abfafad7ba) [想要資料,令牌要先帶來!- JWT](https://ithelp.ithome.com.tw/articles/10250968)