Flask简单调用Redis、MySQL和生成token及token验证

5 篇文章 0 订阅

项目地址:https://github.com/MasonYyp/myflask

1 安装python基础环境

# 安装flask
pip install flask

# 安装redis
pip install redis

# 安装操作MySQL的包
pip install flask-sqlalchemy
pip install pymysql
# 安装序列化包
pip install flask-marshmallow
pip install marshmallow-sqlalchemy


# 安装生成token相关的包
pip install pyjwt

# 安装生成验证码的包
pip install captcha

2 Docker安装redis和mysql

2.1 redis.conf

# redis.conf
# 可远程连接
# bind 127.0.0.1
# 解除保护模式
protected-mode no
# 数据持久化
appendonly yes
# 设置密码
requirepass 123456

2.2 创建redis容器

sudo docker run -itd \
--restart=always \
--name fl_redis \
-p 6379:6379 \
-v /home/fl_redis/redis.conf:/etc/redis/redis.conf \
-v /home/fl_redis/data:/data \
redis:6.2.2 redis-server /etc/redis/redis.conf

2.3 创建MySQL容器

# 安装MySQL,密码是123456
sudo docker run -itd \
--restart=always \
--name fl_mysql \
-p 3306:3306 \
-v /home/mysql/conf:/etc/mysql/conf.d \
-v /home/mysql/data:/var/lib/mysql \
-v /home/mysql/log:/var/log/mysql \
-e MYSQL_ROOT_PASSWORD=123456 \
mysql:5.7

进入MySQL容器创建数据库

create database myflask default character set utf8mb4 collate utf8mb4_unicode_ci;

3 vue前端

3.1 前端页面

<template>
  <div class="login">
    <div>Login</div>
    <div class="lo_cont">
      <div>
        用户名:<el-input v-model="name" placeholder="请输入用户名"></el-input>
      </div>
      
      <div>
        密码:<el-input v-model="pwd" placeholder="请输入密码" show-password></el-input>
      </div>
      
      <div>
        验证码:<img v-bind:src="codeImgSrc"><el-input v-model="code" placeholder="请输入验证码"></el-input>
      </div>
      <el-button v-on:click="login">提交</el-button>
    </div>
  
  </div>
</template>

<script>

import axios from 'axios'

// 按需引入ElementUI组件
import 'element-ui/lib/theme-chalk/index.css' 
import { Input, Button } from 'element-ui'


export default {
  name: 'Login',
  props: {
    msg: String
  },
  components:{
    elInput: Input,
    elButton: Button
  },

  data:function(){
    return{
      name:"mason",
      pwd:"123456",
      code:"",
      codeKey: "",
      codeImgSrc: ""
    }
  },

  created:function(){
    this.get_captcha()
  },

  methods:{

    get_captcha: function(){
      axios.post("/flask/public/captcha").then(res=>{
        console.log(res.data)
        let data = res.data;
        this.codeImgSrc = data.code_img;
        this.codeKey = data.code_key;
      })
    },

    login: function(){
      let param = {
        name: this.name,
        pwd: this.pwd,
        code_key: this.codeKey,
        code: this.code
      }

      // 使用json方式传参
      axios.post("/flask/public/login", param).then(res=>{
        let token = res.data
        console.log(token);
      })
    }
  }
}
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>

.lo_cont>>>.el-input{
  width: 30%;
}

</style>

3.2 截图

前端截图

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UCayOZby-1663496733483)(Flask使用Redis存储tocken.assets/image-20220918172017664.png)]

4 flask工程

4.1 工程目录

在这里插入图片描述

4.2 代码

4.2.1 main
from flask import Flask

from route.interceptor import before_interceptor
from route.operate_blueprint import OperateBlueprint
from dao.base_db.mysql_db import init_mysql_db

app = Flask(__name__)


# 设置拦截器
@app.before_request
def route_interceptor():
    return before_interceptor()


# 初始化蓝本
operate_blueprint = OperateBlueprint(app)
operate_blueprint.init_blueprint()

# 初始化MySQL
init_mysql_db(app)


# Run the app
if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5000)
4.2.2 config目录
4.2.2.1 common.py
import os


def get_root_path():
    cur_path = os.path.abspath(os.path.dirname(__file__))
    root_path, _ = os.path.split(cur_path)

    return root_path
4.2.2.2 db_conf.py

class RedisConf:
    # 配置基本参数
    pwd = "123456"
    host = "192.168.108.100"
    port = 6379


class MysqlConf:
    acc = "root"
    pwd = "123456"
    host = "192.168.108.100"
    port = 3306
    db = "myflask"


redis_conf = RedisConf()
mysql_conf = MysqlConf()
4.2.3 dao目录
4.2.3.1 base_db.mysql_db.py
from flask import Flask
from config.db_conf import mysql_conf
from flask_sqlalchemy import SQLAlchemy
from flask_marshmallow import Marshmallow

# 添加pymysql驱动,连接MySQL数据库
import pymysql
pymysql.install_as_MySQLdb()

# 创建MySQL单实例
mysql_db = SQLAlchemy()

# 创建Schema
mysql_schema = Marshmallow()


# 初始化MySQL数据库
def init_mysql_db(app: Flask):

    # 配置MySQL数据库url
    db_url = "mysql://" + mysql_conf.acc + ":" + mysql_conf.pwd + "@" + mysql_conf.host + ":" + str(mysql_conf.port) + "/" + mysql_conf.db
    app.config["SQLALCHEMY_DATABASE_URI"] = db_url

    # 关闭sqlalchemy自动跟踪数据库
    app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False

    # 显示底层执行的SQL语句
    app.config['SQLALCHEMY_ECHO'] = True

    # 解决‘No application found. Either work inside a view function or push an application context.’
    app.app_context().push()

    # 初始化app
    mysql_db.init_app(app)

    # 初始化schema
    mysql_schema.init_app(app)


# 初始化table
def init_table():
    # 删除表
    mysql_db.drop_all()
    # 创建表
    mysql_db.create_all()
4.2.3.2 base_db.redis_db.py
import redis
from config.db_conf import redis_conf


class RedisDB:

    # 初始化redis
    def __init__(self):
        # 设置主机、端口号和密码
        redis_pool = redis.ConnectionPool(host=redis_conf.host, port=redis_conf.port, password=redis_conf.pwd, decode_responses=True)
        self.__strict_redis = redis.StrictRedis(connection_pool=redis_pool)

    # 在redis中添加键值,并设置过期时间
    def set(self, key, value, expiry):
        self.__strict_redis.set(name=key, value=value, ex=expiry)

    # 获取值
    def get(self, key):
        return self.__strict_redis.get(name=key)

    # 获取键值的剩余时间
    def ttl(self, key):
        # Time To Live
        return self.__strict_redis.ttl(name=key)


# 设置单例模式
redis_db = RedisDB()
4.2.3.3 base_db.mysql_db.model.user_model.py
from dao.base_db.mysql_db import mysql_db as db


# 创建用户表
class User(db.Model):
    # 用户
    __tablename__ = "tf_user"

    id = db.Column(db.Integer, nullable=False, primary_key=True, autoincrement=True)
    name = db.Column(db.String(32), nullable=False, unique=True)
    pwd = db.Column(db.String(32), nullable=False)

    def __repr__(self):
        # 显示对象中的信息
        return "User object: name=%s" % self.name
4.2.3.4 base_db.mysql_db.schema.user_schema.py
from dao.base_db.mysql_db import mysql_schema as ma
from dao.mysql_dao.model.user_model import User

"""
# 序列化方法1
# 需要
注意flask-marshmallow的版本:
    flask-marshmallow<0.12.0
    class AuthorSchema(ma.ModelSchema)
    
    flask-marshmallow>=0.12.0 (recommended)
    class AuthorSchema(ma.SQLAlchemyAutoSchema)
"""

# 使用flask_marshmallow初始化model
class UserSchema(ma.SQLAlchemyAutoSchema):
    class Meta:
        model = User
        load_instance = True


"""

# 序列号方法2
from marshmallow import Schema, fields

# 使用marshmallow初始化model
class UserSchema(Schema):
    id = fields.Integer()
    name = fields.String()
    pwd = fields.String()

    class Meta:
        # 设置序列化字段
        fields = ["id", "name", "pwd"]
        # 转化为有序字典
        ordered = True

"""
4.2.3 route目录
4.2.3.1 interceptor.py
from flask import request
from dao.base_db.redis_db import redis_db


# 设置拦截器
def before_interceptor():
    # Pass white list
    if white_list():
        pass
    else:
        res_status = check_token()
        if res_status == 1:
            pass
        else:
            return res_status


# 设置白名单
def white_list():
    url_white_list = ["/flask/public/captcha", "/flask/public/login", "/flask/public/register", "/flask/public/initdb"]
    cur_url = request.path

    # Pass the url
    if cur_url in url_white_list:
        return True
    else:
        return False


# 设置token
def check_token():
    token = request.headers.get("token")
    if token is None:
        return "Token None"

    token_ttl = redis_db.ttl(token)

    # 验证码已经过期
    if token_ttl <= 0:
        return "Token expired"

    # 获取redis中的code
    redis_token = redis_db.get(token)

    if token != redis_token:
        return "Token error"

    return 1
4.2.3.2 operate_blueprint.py
# Configure Blueprint

from route.public_route import public_bp
from route.userinfo import userinfo_bp


class OperateBlueprint():

    # Init the app
    def __init__(self, app):
        self.__app = app
        self.__base_path = "/flask"

    # 初始化蓝本
    def init_blueprint(self):
        # Register the blueprint
        self.__app_register_blueprint(public_bp)
        self.__app_register_blueprint(userinfo_bp)

    # 在Flask中添加蓝本
    def __app_register_blueprint(self, blueprint):
        self.__app.register_blueprint(blueprint, url_prefix=self.__base_path + blueprint.name)
4.2.3.3 public_route.py
from flask import Blueprint, jsonify, request
from service.public_service import register_user, login_user, generate_captcha, create_table


public_bp = Blueprint("/public", __name__)


@public_bp.route("/captcha", methods=['POST'])
def captcha():
    captcha_dict = generate_captcha()
    return jsonify(captcha_dict)


@public_bp.route("/login", methods=['POST'])
def login():
    # 获取json数据
    data = request.get_json()
    print(data)
    res = login_user(data['name'], data['pwd'], data['code_key'], data['code'])
    return jsonify(res)


@public_bp.route("/register", methods=['POST'])
def register():
    register_user()
    return "register"


@public_bp.route("/initdb", methods=['POST'])
def init_mysql_db():
    return create_table()
4.2.3.4 userinfo.py
from flask import Blueprint
userinfo_bp = Blueprint("/userinfo", __name__)


@userinfo_bp.route("/info", methods=['POST'])
def login():
    return "Mason"
4.2.4 service目录

public_service.py

from dao.base_db.redis_db import redis_db
from dao.base_db.mysql_db import mysql_db, init_table

from dao.mysql_dao.model.user_model import User
from dao.mysql_dao.schema.user_schema import UserSchema

from util.operate_captcha import operate_captcha
from util.operate_token import operate_token


def generate_captcha():
    # 生成验证码
    code = operate_captcha.generate_code()
    # 生成图片验证码
    image_base64_str = operate_captcha.generate_captcha_base64(code)

    # 生成唯一key
    code_key = operate_captcha.generate_code_key()

    # 存入Redis
    redis_db.set(code_key, code.casefold(), 30)

    data = {
        "code_key": code_key,
        "code_img": image_base64_str
    }

    return data


def register_user():
    user = User()
    user.name = "mason"
    user.pwd = "123456"

    # 添加数据
    mysql_db.session.add(user)
    mysql_db.session.commit()


def login_user(name, pwd, code_key, code):

    code_ttl = redis_db.ttl(code_key)

    # 验证码已经过期
    if code_ttl <= 0:
        return -1

    # 获取redis中的code
    redis_code = redis_db.get(code_key)

    # 验证码过期
    if redis_code != code.casefold():
        return -2

    # 查询数据
    user = User.query.filter_by(name=name, pwd=pwd).first()

    # 序列号对象
    user_json = UserSchema().dump(user)
    print(user_json)

    # 生成token
    token = operate_token.create_token(user.id, user.name, 60)

    # 添加到redis
    redis_db.set(token, token, 60)

    return token


def create_table():
    init_table()
    return "create table"
4.2.5 util目录
4.2.5.1 operate_captcha.py
from captcha.image import ImageCaptcha
import random

import base64
import uuid


class OperateCaptcha:

    def generate_code(self, code_len=4):
        code_chars = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
        chars_len = len(code_chars) - 1
        code = ''
        for _ in range(code_len):
            index = random.randint(0, chars_len)
            code = code + code_chars[index]
        return code

    def generate_captcha_base64(self, code):
        # 生成验证码
        image = ImageCaptcha()

        # 生成图片BytesIO
        img_bytes_io = image.generate(code)
        # 转化为字符串
        image_base64 = base64.b64encode(img_bytes_io.read())
        image_base64_str = "data:image/png;base64," + str(image_base64, 'utf-8')
        return image_base64_str

    def generate_code_key(self):
        # 生成唯一标识
        code_key = str(uuid.uuid1())
        return code_key


operate_captcha = OperateCaptcha()

4.2.5.2 operate_token.py
import jwt
from datetime import datetime, timedelta


# 操作Token
class OperateToken:

    def __init__(self):
        self._private_key = "123456"
        pass

    # 创建token
    def create_token(self, user_id, user_name, expiry_seconds):
        # Calculate expiry time
        expiry_time = datetime.utcnow() + timedelta(seconds=expiry_seconds)

        payload = {
            'exp': expiry_time,
            'user_id': user_id,
            'username': user_name
        }

        # 加密
        encode_jwt = jwt.encode(payload, self._private_key, algorithm='HS256')
        return encode_jwt

    # 验证token
    def check_token(self, token):
        decode_jwt = "-1"
        # 解密
        try:
            decode_jwt = jwt.decode(token, self._private_key, algorithms=['HS256'])
        except jwt.PyJWTError:
            print("Token is error!")
        return decode_jwt


operate_token = OperateToken()

5 postman截图

5.1 初始化数据表
http://127.0.0.1:5000/flask/public/initdb

5.2 注册接口
http://127.0.0.1:5000/flask/public/register

在这里插入图片描述

5.3 调用info接口

调用权限口需要验证token

http://127.0.0.1:5000/flask/userinfo/info

在这里插入图片描述

  • 4
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值