後端使用flask設計基於token認證方式的restful接口,前端使用vue.js全家桶,利用axios通信。html
感謝兩篇文章的做者:前端
源碼連接:https://github.com/xingyys/fl...vue
Flask採用token認證方式,主要思路是經過/api/login
登陸獲取token
,而後使用token
調用各個接口。
所用到框架的庫:python
flask/ ├── app # 主目錄 │ ├── __init__.py │ ├── __init__.pyc │ ├── models.py # 數據庫 │ ├── models.pyc │ ├── views.py # 視圖 │ └── views.pyc ├── config.py # 配置信息 ├── config.pyc ├── db_create.py # 建立數據庫 ├── db_migrate.py # 更新數據庫 ├── db_repository │ ├── __init__.py │ ├── __init__.pyc │ ├── manage.py │ ├── migrate.cfg │ ├── README │ └── versions │ ├── 008_migration.py │ ├── 008_migration.pyc │ ├── 009_migration.py │ ├── 009_migration.pyc │ ├── __init__.py │ └── __init__.pyc ├── index.html └── run.py # app的運行文件
app/__init__.py
# -*- coding:utf-8 -*- from flask import Flask from flask_sqlalchemy import SQLAlchemy from flask_httpauth import HTTPBasicAuth from flask_cors import CORS app = Flask(__name__) # flask的跨域解決 CORS(app) app.config.from_object('config') db = SQLAlchemy(app) auth = HTTPBasicAuth() from . import models, views
config.py
import os basedir = os.path.abspath(os.path.dirname(__file__)) SQLALCHEMY_DATABASE_URI = "mysql://root:123456@127.0.0.1/rest" SQLALCHEMY_MIGRATE_REPO = os.path.join(basedir, 'db_repository') SQLALCHEMY_TRACK_MODIFICATIONS = True BASEDIR = basedir # 安全配置 CSRF_ENABLED = True SECRET_KEY = 'jklklsadhfjkhwbii9/sdf\sdf'
環境中使用mysql
數據庫,版本爲mariadb 10.1.22
。建立rest
表mysql
$ mysql -uroot -p xxxxxx $ create database rest default charset utf8;
app/models.py
# -*- coding:utf-8 -*- from app import db, app from passlib.apps import custom_app_context from itsdangerous import TimedJSONWebSignatureSerializer as Serializer, SignatureExpired, BadSignature class User(db.Model): __tablename__ = 'users' id = db.Column(db.Integer, primary_key=True) username = db.Column(db.String(32), index=True) password = db.Column(db.String(128)) # 密碼加密 def hash_password(self, password): self.password = custom_app_context.encrypt(password) # 密碼解析 def verify_password(self, password): return custom_app_context.verify(password, self.password) # 獲取token,有效時間10min def generate_auth_token(self, expiration = 600): s = Serializer(app.config['SECRET_KEY'], expires_in = expiration) return s.dumps({ 'id': self.id }) # 解析token,確認登陸的用戶身份 @staticmethod def verify_auth_token(token): s = Serializer(app.config['SECRET_KEY']) try: data = s.loads(token) except SignatureExpired: return None # valid token, but expired except BadSignature: return None # invalid token user = User.query.get(data['id']) return user
建立數據庫users
表:ios
$ python db_create.py $ python db_migrate.py
app/views.py
from app import app, db, auth from flask import render_template, json, jsonify, request, abort, g from app.models import * @app.route("/") @auth.login_required def index(): return jsonify('Hello, %s' % g.user.username) @app.route('/api/users', methods = ['POST']) def new_user(): username = request.json.get('username') password = request.json.get('password') if username is None or password is None: abort(400) # missing arguments if User.query.filter_by(username = username).first() is not None: abort(400) # existing user user = User(username = username) user.hash_password(password) db.session.add(user) db.session.commit() return jsonify({ 'username': user.username }) @auth.verify_password def verify_password(username_or_token, password): if request.path == "/api/login": user = User.query.filter_by(username=username_or_token).first() if not user or not user.verify_password(password): return False else: user = User.verify_auth_token(username_or_token) if not user: return False g.user = user return True @app.route('/api/login') @auth.login_required def get_auth_token(): token = g.user.generate_auth_token() return jsonify(token)
用戶註冊後密碼加密存儲,確認用戶身份時密碼解密。須要認證的api
上添加@auth.login_required
,它會在調用接口以前調用@auth.verify_password
下的方法(此方法惟一)如verify_password
。根據請求的路徑選擇不一樣的認證方式。git
使用curl命令測試接口github
註冊用戶:vue-router
$ curl -i -X POST -H "Content-Type: application/json" -d '{"username":"admin","password":"123456"}' http://127.0.0.1:5000/api/register HTTP/1.0 200 OK Content-Type: application/json Access-Control-Allow-Origin: * Content-Length: 26 Server: Werkzeug/0.12.2 Python/2.7.13 Date: Wed, 20 Sep 2017 06:33:46 GMT { "username": "admin" }
查看數據庫:sql
MariaDB [rest]> select * from users\G; *************************** 1. row *************************** id: 1 username: admin password: $6$rounds=656000$etV4F3xLL0dwflX8$mLFX9l5dumBnQFtajGmey346viGuQ4bxR7YhQdKtB/nQH9ij2e3HHMEBPj.ef/o//4o9P2Wd3Y7dxQfjwR2hY/ 1 row in set (0.00 sec)
獲取token:
curl -i -u admin:123456 -X GET -H "Content-Type: application/json" http://127.0.0.1:5000/api/login HTTP/1.0 200 OK Content-Type: application/json Access-Control-Allow-Origin: * Content-Length: 125 Server: Werkzeug/0.12.2 Python/2.7.13 Date: Wed, 20 Sep 2017 06:37:01 GMT "eyJhbGciOiJIUzI1NiIsImV4cCI6MTUwNTg5MDAyMSwiaWF0IjoxNTA1ODg5NDIxfQ.eyJpZCI6MX0.nUIKq-ZhFOiLPwZyUmfgWPfHYNy8o6eoR6lmzdsY0oQ"
使用token調用api:
$ curl -i -u eyJhbGciOiJIUzI1NiIsImV4cCI6MTUwNTg5MDAyMSwiaWF0IjoxNTA1ODg5NDIxfQ.eyJpZCI6MX0.nUIKq-ZhFOiLPwZyUmfgWPfHYNy8o6eoR6lmzdsY0oQ:unused -X GET -H "Content-Type: application/json" http://127.0.0.1:5000/ HTTP/1.0 200 OK Content-Type: application/json Access-Control-Allow-Origin: * Content-Length: 15 Server: Werkzeug/0.12.2 Python/2.7.13 Date: Wed, 20 Sep 2017 06:38:22 GMT "Hello, admin"
基於token
的Flask api
成功!!!!
前端使用vue
的全家桶,axios先後端通信,axios攔截器,localStorage保存token
所使用的框架和庫:
main.js
// 初始化axios axios.defaults.baseURL = 'http://127.0.0.1:5000' axios.defaults.auth = { username: '', password: '', } // axios.interceptors.request.use((config) => { // console.log(config) // return config; // }, (error) => { // return Promise.reject(error) // }) // axios攔截器,401狀態時跳轉登陸頁並清除token axios.interceptors.response.use((response) => { return response; }, (error) => { if (error.response) { switch (error.response.status) { case 401: store.commit('del_token') router.push('/login') } } return Promise.reject(error.response.data) }) // 路由跳轉 router.beforeEach((to, from, next) => { if (to.meta.required) { // 檢查localStorage if (localStorage.token) { store.commit('set_token', localStorage.token) // 添加axios頭部Authorized axios.defaults.auth = { username: store.state.token, password: store.state.token, } // iview的頁面加載條 iView.LoadingBar.start(); next() } else { next({ path: '/login', }) } } else { iView.LoadingBar.start(); next() } }) router.afterEach((to, from, next) => { iView.LoadingBar.finish(); })
export default new Router({ routes: [{ path: '/', name: 'index', component: Index, meta: { required: true, } }, { path: '/login', name: 'login', component: Login, }] })
路由添加meta
字段,做爲須要認證路由的標誌
export default new Vuex.Store({ state: { token: '' }, mutations: { set_token(state, token) { state.token = token localStorage.token = token }, del_token(state) { state.token = '' localStorage.removeItem('token') } } })
vuex
中保存token
,同時修改刪除token
和localStorage.token
登陸:
handleSubmit(name, form) { this.$refs[name].validate((valid) => { if (valid) { // 用戶名密碼簡單驗證後添加到axios的auth中 this.$axios.defaults.auth = { username: form.username, password: form.password, } this.$axios.get('/api/login').then(response => { this.$Message.success("提交成功") let data = response.data // 保存token this.$store.commit('set_token', data) this.$router.push('/') }).catch(error => { this.$Message.error(error.status) }) } else { this.$Message.error('表單驗證失敗!'); } }) }
登出:
logout() { this.$store.commit('del_token') this.$router.push('/login') }
刪除token
並跳轉到登陸頁
flask
和vue
的token
認證就完成了!!!!