本文主要展示如何用flask实现token验证及跨域问题,本文主要参考文章为:Flask实现token认证 - 简书
但基于此,更详细的实现了包括解决频繁options问题、全局请求token验证问题。
run.py
代码中分隔符(======)后的可以略过。
from flask import Flask, g, jsonify, redirect, request, make_response
from flask_httpauth import HTTPBasicAuth
from itsdangerous import TimedJSONWebSignatureSerializer as Serializer
from itsdangerous import BadSignature, SignatureExpired
from flask_cors import CORS
import mysql.connector as mc
import configparser
import re
# 读取配置文件
config = configparser.ConfigParser()
config.read('config.conf', encoding='utf8')
SECRET_KEY = config['SECRET_KEY']['SECRET_KEY']
db_host = config['db']['host']
db_database = config['db']['database']
db_user = config['db']['user']
db_password = config['db']['password']
app = Flask(__name__)
CORS(app, supports_credentials=True, max_age=6000) # max_age设置全局请求的Options预检请求最长时间600s
auth = HTTPBasicAuth()
@app.before_request
@auth.login_required
def is_login():
# print("start")
# 如果是login,可以通过白名单,获取的token后续可以放在自定义请求头里
if request.path == '/login':
return None
if request.method == 'POST':
# print("POST")
return None
if request.method == 'GET':
# print("GET")
return None
# 生成token, 有效时间为60min
def generate_auth_token(user_name, expiration=3600):
s = Serializer(SECRET_KEY, expires_in=expiration)
return s.dumps({'user_name': user_name})
# 解析token
def verify_auth_token(token):
s = Serializer(SECRET_KEY)
# token正确
try:
data = s.loads(token)
# print(data)
return data
# token过期
except SignatureExpired:
return None
# token错误
except BadSignature:
return None
# 连接数据库操作,查看保存的用户名和密码
def connect(user_name, password):
conn = mc.connect(host=db_host, user=db_user, passwd=db_password, database=db_database)
cursor = conn.cursor(dictionary=True)
args = (user_name, password)
verify_sql = "SELECT user_name FROM account WHERE user_name = %s AND password = %s;"
cursor.execute(verify_sql, args)
user = cursor.fetchone()
# print(user)
conn.close()
return user
# 验证token
@auth.verify_password
def verify_password(username, password):
# 先验证token
user_id = re.sub(r'^"|"$', '', username)
user_id = verify_auth_token(user_id)
if not user_id: # 如果token不存在,验证用户id与密码是否匹配
user_id = connect(username, password)
# 如果用户id与密码对应不上,返回False
if not user_id:
return False
else: # 如果用户名与密码匹配,则把用户名存到g.user_id这个全局变量里去,后续数据查询可用
g.user_id = user_id.get('user_name')
# print("数据库查询结果:")
# print(g.user_id)
return True
if user_id: # 如果token存在,则将token验证返回的user_name数据存到g.user_id这个全局变量里去,后续数据查询可用
g.user_id = user_id.get('user_name')
# print("传入的不是用户名是token:")
# print(g.user_id)
return True
@app.route('/login', methods=['GET'])
@auth.login_required
def login():
token = generate_auth_token(g.user_id)
return jsonify({'token': str(token)})
@app.route('/index')
def index():
return None
@app.route('/logout')
def logout(): # 用来做登出用,后端返回200前端清除token。
return "200"
@app.route('/getUserInfo', methods=['GET'])
def getUserInfo(): # 用来做后端鉴权用,如果返回200说明鉴权通过。
# print("/getUserInfo start")
# if g.user_id:
# print("this getUserInfo user_id: "+g.user_id)
return "200"
# ==============================================================================
# 以下把消息推送功能(msg)实现api,后续再优化后端代码结构
@app.route('/msg/handleCreate', methods=['POST'])
def msg_handleCreate(): # 创建消息推送配置,参数待定
# print("/getUserInfo start")
return "200"
@app.route('/msg/handleLook', methods=['GET'])
def msg_handleLook(): # 查看消息推送配置,参数待定,后端分页,后端返回json数组
# print("/getUserInfo start")
return "200"
@app.route('/msg/handleEdit', methods=['POST'])
def msg_handleEdit(): # 编辑消息推送配置,参数待定
# print("/getUserInfo start")
return "200"
@app.route('/msg/handleDelete', methods=['POST'])
def msg_handleDelete(): # 删除消息推送配置,参数待定
# print("/getUserInfo start")
return "200"
if __name__ == '__main__':
app.run()