0.前端部分依然基於VueCLI (https://cli.vuejs.org/zh/)
1.創建hello-login文件夾,然后再此文件夾內執行 vue create front-end ,一頓狂回車后,如下圖所示:

2.安裝elementUI,axios,js-cookie,qs
2.1 npm i element-ui -S (https://element.eleme.cn/#/zh-CN/component/installation)
2.2 npm install --save axios vue-axios (http://www.axios-js.com/zh-cn/docs/vue-axios.html)
2.3 npm install js-cookie --save (https://www.npmjs.com/package/js-cookie)
2.4 npm install qs --save (https://www.npmjs.com/package/qs)
3.打開main.js,把elementUI和axios加載。搞定這塊代碼,npm run serve,試試能否正常把項目跑起來。(這是一種編碼方式,安裝組件算是破壞性的動作,需要勤於測試。以免后期跪了)
import Vue from 'vue' import App from './App.vue' import ElementUI from 'element-ui' import 'element-ui/lib/theme-chalk/index.css' import axios from 'axios' import VueAxios from 'vue-axios' //Vue.config.productionTip = false axios.defaults.withCredentials = true Vue.use(VueAxios,axios) Vue.use(ElementUI) new Vue({ render: h => h(App), }).$mount('#app')
4.在components目錄下 創建Login.vue文件
<template> <div class='login'> <h1>{{ titleMsg }}</h1> <el-form ref="loginForm" :model="loginData" label-width="100px"> <el-form-item label="用戶名" prop="username" :rules="[{required: true, message: '用戶名不能為空'}]"> <el-input ref="username" type="password" v-model="loginData.username" autocomplete="off"></el-input> </el-form-item> <el-form-item label="密碼" prop="password" :rules="[{required: true, message: '密碼不能為空'}]"> <el-input type="password" v-model="loginData.password" autocomplete="off"></el-input> </el-form-item> <el-form-item> <el-button type="primary" @click="submitForm('loginForm')">提交</el-button> <el-button @click="resetForm('loginForm')">重置</el-button> </el-form-item> </el-form> </div> </template> <script> export default { name: 'loginForm', data() { return { titleMsg: '歡迎來到旗幟世界', loginData: { username: '', password: '' } } }, methods: { submitForm(formName) { this.$refs[formName].validate((valid) => { if (valid) { console.log('submit') } else { console.log('illegad submit!!'); return false; } }) }, resetForm(formName) { this.$refs[formName].resetFields() console.log('reset') } } } </script>
5.打開App.vue,將helloworld相關代碼注釋,改寫成Login。這種操作可以加深理解vue的組件機制。為后期學習使用router打基礎
<template> <div id="app"> <img alt="Vue logo" src="./assets/logo.png"> <!-- <HelloWorld msg="Welcome to Your Vue.js App"/> --> <Login/> </div> </template> <script> // import HelloWorld from './components/HelloWorld.vue' import Login from './components/Login.vue' export default { name: 'App', components: { //HelloWorld Login } } </script> <style> #app { font-family: Avenir, Helvetica, Arial, sans-serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; text-align: center; color: #2c3e50; margin-top: 60px; } </style>
6.npm run serve 跑起來后如下圖。
7.接下來,使用axios把此表單提交到py搭建的后台並返回消息。先轉到py端,把后端代碼實現一部分。在完成這個功能吧。
--------------------------------------------------------------------------------------------------------------------------------------------------------------
0.后端實現,使用flask(https://dormousehole.readthedocs.io/en/latest/)
1.在hello-login文件夾下創建 back-end文件夾,並運行命令行 py -m venv venv 這時就會在當前目錄下創建venv虛擬機 (https://dormousehole.readthedocs.io/en/latest/installation.html#id4)
2.執行venv\scripts\activate 這樣就啟動了虛擬機環境 
3.開始安裝flask, flask-login, flask-cors, jwt 。注意都要在venv虛擬機環境下安裝
3.1 pip install Flask (https://dormousehole.readthedocs.io/en/latest/installation.html#flask)
3.2 pip install flask-login (https://flask-login.readthedocs.io/en/latest/)
3.3 pip install pyjwt (https://pypi.org/project/PyJWT/)
3.4 pip install -U flask-cors (https://flask-cors.readthedocs.io/en/latest/)
4.在當前目錄下創建app.py文件 敲入代碼:
from flask import Flask import json app = Flask(__name__) app.secret_key =b'\x15f\x07\xd3\xd9\xbf*\x82\xd1\xe6\xb4\xf2\x95\xdd\x8f\x12' #命令行中運行后拷貝出隨機值 python -c "import os; print(os.urandom(16))" @app.route('/hello') def helloworld(): returnData = {'code': 0, 'msg': 'success', 'data': 'hello world' } return json.dumps(returnData),200 if __name__ == '__main__': app.run(debug = True)
5.在venv虛擬機下 運行py app.py 然后再瀏覽器中查看 http://localhost:5000/hello 。這說明基本框架已經構建成功。
之所以返回如下格式,是參考了這篇博文(https://sobird.me/http-json-api-guide.htm),原始出處並未找到,
6.創建用戶單元.user.py。實現了flask_login (https://flask-login.readthedocs.io/en/latest/index.html#your-user-class)所提及的功能。以及用USERS字典暫時代替未來的數據庫表。
from flask_login import UserMixin from werkzeug.security import check_password_hash,generate_password_hash USERS = [ { "id":1, "name":"admin", "password":generate_password_hash('123') }, { "id":2, "name":"李四", "password":generate_password_hash('123') }, ] class User(UserMixin): def __init__(self,user): self.username = user.get("name") self.password_hash = user.get("password") self.id = user.get("id") @staticmethod def queryUser(username): for user in USERS: if (user.get('name') == username) : return User(user) return None def verifyPassword(self,password): if self.password_hash is None: return False return check_password_hash(self.password_hash,password) def get_id(self): return self.id def get(user_id): if not user_id: return None for user in USERS: if str(user.get('id')) == str(user_id) : return User(user) print('None') return None
7.創建jwt操作單元jwt_token.py 。實現了對jwt的簡單二次封裝。其實不做封裝也可以
7.1 JWT中 “Registered claims” 包含 iss(發行者),exp(到期時間),sub(主題),aud(受眾)是官方建議攜帶的。我偷懶只采用了到期時間這一個聲明。
7.2 jwt可以參考官網(https://jwt.io/)介於國內強大的長城。此官網偶發型能打開。 亦可參考此博文 https://www.cnblogs.com/mantoudev/p/8994341.html
from jwt import jwt,PyJWTError from datetime import datetime,timedelta SECRECT_KEY = b'\x92R!\x8e\xc6\x9c\xb3\x89#\xa6\x0c\xcb\xf6\xcb\xd7\xbc' def genToken(data): expInt = datetime.utcnow() + timedelta(seconds=3) payload = { 'exp': expInt, 'data': data } token = jwt.encode(payload,key= SECRECT_KEY,algorithm= 'HS256') return bytes.decode(token) def verfiyToken(tokenStr): try: tokenBytes = tokenStr.encode('utf-8') payload = jwt.decode(tokenBytes,key= SECRECT_KEY,algorithm= 'HS256') return payload except PyJWTError as e: print("jwt驗證失敗: %s" % e) return None
8.創建登錄邏輯單元 login.py 。同前端的主要交互邏輯都在此處。flask_login的具體實現(https://flask-login.readthedocs.io/en/latest/index.html#installation)
import time import json from flask import Blueprint,request from flask_login import LoginManager,login_user,logout_user,login_required,current_user from user import User,USERS from jwt_token import genToken,verfiyToken login_page = Blueprint('login_page',__name__) login_manager = LoginManager() login_manager.login_view = 'helloworld' @login_page.record_once def on_load(state): login_manager.init_app(state.app) # @login_manager.user_loader # def load_user(user_id): # return User.get(user_id) @login_manager.request_loader def load_user_from_request(request): token = request.headers.get('Authorization') if token == None: return None payload = verfiyToken(token) if payload != None: user = User.queryUser(payload['data']['username']) else: user = None return user @login_page.route('/first') @login_required def firstPage(): returnData = {'code': 0, 'msg': 'success', 'data': 'First Blood(來自' + current_user.username +')' } return returnData,200 @login_page.route('/login', methods=['POST']) def login(): if request.method == 'POST': username = request.form['username'] password = request.form['password'] user = User.queryUser(username) if (user != None) and (user.verifyPassword(password)) : login_user(user) token = genToken({'username':username,'password':'******'}) returnData = {'code': 0, 'msg': 'success', 'data': {'token':token} } return json.dumps(returnData),200 else : returnData = {'code': 1, 'msg': 'success', 'data': 'username or password is not correct' } return json.dumps(returnData),200 @login_page.route('/logout') @login_required def logout(): username = current_user.username logout_user() returnData = {'code': 0, 'msg': 'success', 'data': ' Bye ' + username } return json.dumps(returnData),200
9.執行py app.py 后,在postman 分別測試如下鏈接(注意圖中紅框內容。):
9.1 http://127.0.0.1:5000/hello
9.2 http://127.0.0.1:5000/login
9.3 http://127.0.0.1:5000/first
9.4 http://127.0.0.1:5000/logout 



10.后端代碼初步結束。下一階段,連接前后端
--------------------------------------------------------------------------------------------------------------------------------------------------
1.打開front-end項目,用axios把后端接口調用起來
2.在項目src目錄下 創建文件夾 utils 然后在其內創建文件request.js。這里對axios做了簡單封裝。
import axios from 'axios' import Cookies from 'js-cookie' /****** 創建axios實例 ******/ const service = axios.create({ baseURL: 'http://localhost:5000', // api的base_url timeout: 5000 // 請求超時時間 }) service.interceptors.request.use( config => { config.headers['Authorization'] = Cookies.get('Authorization') return config }, error => { console.log(error) return Promise.reject(error) } ) /****** respone攔截器==>對響應做處理 ******/ // service.interceptors.response.use( // response => { // console.log(response) // //這里根據后端提供的數據進行對應的處理 // if (response.data.result === 'TRUE') { // return response.data; // } // }, // error => { // console.log(error); // return Promise.reject(error) // } // ) export default service;
<template> <div class='login'> <h1>{{ titleMsg }}</h1> <el-form ref="loginForm" :model="loginData" label-width="100px"> <el-form-item label="用戶名" prop="username" :rules="[{required: true, message: '用戶名不能為空'}]"> <el-input ref="username" type="password" v-model="loginData.username" autocomplete="off"></el-input> </el-form-item> <el-form-item label="密碼" prop="password" :rules="[{required: true, message: '密碼不能為空'}]"> <el-input type="password" v-model="loginData.password" autocomplete="off"></el-input> </el-form-item> <el-form-item> <el-button type="primary" @click="loginForm('loginForm')">提交</el-button> <el-button @click="resetForm('loginForm')">重置</el-button> </el-form-item> <el-form-item> <el-button @click="testForm()">測試</el-button> <el-button @click="logoutForm()">登出</el-button> </el-form-item> </el-form> </div> </template> <script> import qs from 'qs' import service from '../utils/request' import Cookies from 'js-cookie' export default { name: 'loginForm', data() { return { titleMsg: '歡迎來到旗幟世界', loginData: { username: '', password: '' } } }, methods: { loginForm(formName) { this.$refs[formName].validate((valid) => { if (valid) { service({url: '/login',method: 'post',data: qs.stringify(this.loginData)}) .then(response => { const { data } = response Cookies.set('Authorization',data.data.token) alert('submit!!!' +'\n'+ data.msg) }) .catch(error => { console.log(error) }) } else { console.log('illegad submit!!'); return false; } }) }, testForm() { service({url: '/first',method: 'get'}) .then(response => { const { data } = response alert('firstPage!!!' +'\n'+ data.data) }) .catch(error => { console.log(error) }) }, logoutForm() { service({url: '/logout',method: 'get'}) .then(response => { const { data } = response alert('logout!!!' +'\n'+ data.data) }) .catch(error => { console.log(error) }) } } } </script>
3.npm run serve 后,測試。四個按鈕
4.其中關注一個狀況,當登出后,再次點擊測試。測試依然返回成功。這就出現一個問題,登出功能無效,回看后端代碼logout是正常運作。
稍加分析,即可得出產生這種情況的原因是jwt Token本身的弊端。前文的連接中已經提醒。如何解決此狀況,日后再分析
5.收工了。
