1 主要思路
1. 前端編寫導航守衛,如果沒有localStorage中沒有獲取到token,則跳轉登錄頁。
2. 登錄頁,向后端登錄發送,獲取token,然后將token存儲在localStorage中,跳轉首頁。
3. 在前端請求攔截器上加上為header加上token,如果有的話。
4. 后端的登錄接口,驗證完賬號密碼后,用itsdangerous工具提供的方法生成token,將用戶名dump到token中,將此token返回前端。
5. 后端的全局請求驗證器(before_request)上,從header中獲取出token,用工具解析出token中的用戶名,通過request域傳遞到待執行的函數。
2 相關代碼
router.js
import Vue from 'vue' import Router from 'vue-router' import Home from './components/Home.vue' Vue.use(Router); const router = new Router({ routes: [ { path: '/', name: 'home', component: Home }, { path: '/about', name: 'about', // route level code-splitting // this generates a separate chunk (about.[hash].js) for this route // which is lazy-loaded when the route is visited. component: () => import(/* webpackChunkName: "about" */ './components/About.vue') }, { path: '/login', name: 'login', component: () => import('./components/Login.vue') }, ] }); // 導航守衛 // 使用 router.beforeEach 注冊一個全局前置守衛,判斷用戶是否登陸 router.beforeEach((to, from, next) => { if (to.path === '/login') { next(); } else { let token = localStorage.getItem('Authorization'); if (!token) { next('/login'); } else { next(); } } }); export default router;
request.js
import axios from 'axios' // 創建 axios 實例 axios.defaults.headers = { 'Content-Type': 'application/json' }; const service = axios.create({ timeout: 5000 // 請求超時時間 }); // request interceptor 請求攔截器,如果有token則在請求上加上token service.interceptors.request.use( config => { if (localStorage.getItem('Authorization')) { config.headers.token = localStorage.getItem('Authorization'); } return config; }, error => { return Promise.reject(error); }); // response interceptor 返回攔截器 service.interceptors.response.use( response => { //如果返回401,則清除token,跳轉登錄 if (response.data.code === 401) { localStorage.removeItem('Authorization'); return this.$router.push('/login'); } return response }, error => { console.log('error' + error) // for debug return Promise.reject(error) }); export default service
Login.vue
<template> <div> <div> <input type="text" v-model="loginForm.username" placeholder="用戶名"/> <input type="text" v-model="loginForm.password" placeholder="密碼"/> <button @click="login">登錄</button> </div> </div> </template> <script> import { mapMutations } from 'vuex'; import { login } from '@/api' import { showMessage } from '@/utils' export default { name: 'Login', components: {}, data () { return { loginForm: { username: '', password: '' } } }, methods: { ...mapMutations(['changeLogin']), //這樣聲明后,可以直接使用this.changeLogin 調用定義在store中的mutations中的方法 login () { if (this.loginForm.username === '' || this.loginForm.password === '') { alert('賬號或密碼不能為空'); } else { login(this.loginForm).then(response => { if (response.data.code === "0000") { this.changeLogin({Authorization: response.data.token}); this.$router.push('/'); } else if (response.data.code === "0001") { showMessage(response.data.msg,"error") }else{ showMessage("發生系統級錯誤,請聯系110","error") } }) } } } } </script>
后端代碼
def login(): result = {"code": "0003"} username = request.json.get("username") password = request.json.get("password") user = models.User.filter(username=username).first() if user and user.password != password: # 密碼錯誤 result["code"] = "0001" result["msg"] = "您可能忘記了密碼" return jsonify(result) if not user: # 用戶不存在,直接創建新用戶,同時創建默認的project newUser = models.User.create(username=username, password=password) models.Project.create(name="default", user_id=newUser.id) logger.info("創建用戶:{}成功,創建項目:{}成功".format(username, "default")) token = generate_auth_token(username) result["token"] = token return jsonify(result) ########################################################################################## # 用於生成和驗證token from itsdangerous import TimedJSONWebSignatureSerializer as Serializer, SignatureExpired, BadSignature SECRET_KEY = "SECRET_KEY" def generate_auth_token(data,expiration=3600 * 24 * 30): """ 生成token :param data: 用於加密在token中的數據 :param expiration: 過期時間 :return: """ s = Serializer(SECRET_KEY, expires_in=expiration) return s.dumps(data).decode("utf-8") def verify_auth_token(token): """ 驗證token :param token: 包含數據的token :return: 返回token中包含的數據 """ s = Serializer(SECRET_KEY) try: data = s.loads(token) except SignatureExpired as e: return None # valid token, but expired except BadSignature as e: return None # invalid token return data ########################################################################################## # 此模塊是為了便於管理url from flask import request, session, jsonify from mainApp.api import views from mainApp.settings import NOT_LOGIN_CODE from mainApp.utils.its import verify_auth_token class Router(object): app = None def __init__(self): pass def init(self, app): self.app = app self.add_url_rule() self.register_before_request() # 注冊路由攔截 def path(self, rule, view_func, endpoint=None): self.app.add_url_rule(rule, endpoint, view_func, methods=["GET", "POST"]) def is_login(self): if request.path == "/login" or request.path == "/logout": return None token = request.headers.get("token") verify_value = verify_auth_token(token) if verify_value: request.username = verify_value else: return jsonify({"code": NOT_LOGIN_CODE}) def register_before_request(self): self.app.before_request(self.is_login) def add_url_rule(self): path = self.path path('/', views.home) path('/login', views.login) path('/<method>', views.run) path('/mock/<path:i>', views.mock)