使用Flask开发简单接口(4)--借助Redis实现token验证

前言

在以前咱们已开发了几个接口,而且能够正常使用,那么今天咱们将继续完善一下。咱们注意到以前的接口,都是不须要进行任何验证就可使用的,其实咱们可使用 token ,好比设置在修改或删除用户信息的时候须要进行 token 登陆验证,这个地方咱们将引入 Redis 用于存储登陆时产生的 tokenpython

本人环境:Python 3.7.0Redis 5.0.8git

新增Redis配置

咱们在项目根路径下 config 包中,修改文件 setting.py ,在该文件中配置 Redis 的服务器地址、端口、密码、token过时时间(单位:秒)等参数。github

# Redis配置
REDIS_HOST = "192.168.89.128"
REDIS_PORT = 6379
REDIS_PASSWD = "123456"
# token过时时间(单位:秒)
EXPIRE_TIME = 600

Python操做Redis

  • 安装redis模块

使用Python来操做Redis,须要用到 redis 这个第三方库,具体安装方法以下:redis

pip install redissql

我这里安装的版本是 3.4.1数据库

D:\>pip3 show redis
Name: redis
Version: 3.4.1
Summary: Python client for Redis key-value store
Home-page: https://github.com/andymccurdy/redis-py
Author: Andy McCurdy
Author-email: sedrik@gmail.com
License: MIT
Location: d:\python\installation\lib\site-packages
Requires:
Required-by:
  • 封装Python操做Redis的代码

咱们在项目根路径下 common 包中,新建文件 redis_operate.py ,该文件下简单封装了Python操做Redis的代码,后续将经过调用该文件的 redis_db 对象及方法来操做Redis。json

import redis
from config.setting import REDIS_HOST, REDIS_PORT, REDIS_PASSWD, EXPIRE_TIME


class RedisDb():

    def __init__(self, host, port, passwd):
        # 创建数据库链接
        self.r = redis.Redis(
            host=host,
            port=port,
            password=passwd,
            decode_responses=True # get() 获得字符串类型的数据
        )

    def handle_redis_token(self, key, value=None):
        if value: # 若是value非空,那么就设置key和value,EXPIRE_TIME为过时时间
            self.r.set(key, value, ex=EXPIRE_TIME)
        else: # 若是value为空,那么直接经过key从redis中取值
            redis_token = self.r.get(key)
            return redis_token


redis_db = RedisDb(REDIS_HOST, REDIS_PORT, REDIS_PASSWD)

在Redis中,咱们存储数据是 str 字符串类型,但因为python3与redis交互的驱动问题,默认经过 get() 取出来是 bytes 字节类型,为解决这个问题,咱们在上面代码建立链接时,设置了 decode_responses=True ,这样经过 get() 取出来就是 str 字符串类型。flask

登陆时设置token

通常状况,咱们登陆成功以后,才会产生token,以便在请求其余接口时进行登陆验证。所以,咱们须要在返回登陆成功信息以前,设置token,并把当前登陆用户和设置token,做为redis中的 keyvalue ,放到redis中进行存储。修改用户登陆接口以下:服务器

@app.route("/login", methods=['POST'])
def user_login():
    """登陆用户"""
    username = request.values.get("username", "").strip()
    password = request.values.get("password", "").strip()
    if username and password: # 注意if条件中空串 "" 也是空, 按False处理
        sql1 = "SELECT username FROM user WHERE username = '{}'".format(username)
        res1 = db.select_db(sql1)
        print("查询到用户名 ==>> {}".format(res1))
        if not res1:
            return jsonify({"code": 1003, "msg": "用户名不存在!!!"})
        sql2 = "SELECT * FROM user WHERE username = '{}' and password = '{}'".format(username, password)
        res2 = db.select_db(sql2)
        print("获取 {} 用户信息 == >> {}".format(username, res2))
        if res2:
            timeStamp = int(time.time()) # 获取当前时间戳
            token = "{}{}".format(username, timeStamp)
            redis_db.handle_redis_token(username, token) # 把token放到redis中存储
            login_info = { # 构造一个字典,将 id/username/token/login_time 返回
                "id": res2[0]["id"],
                "username": username,
                "token": token,
                "login_time": time.strftime("%Y/%m/%d %H:%M:%S")
            }
            return jsonify({"code": 0, "login_info": login_info, "msg": "恭喜,登陆成功!"})
        return jsonify({"code": 1002, "msg": "用户名或密码错误!!!"})
    else:
        return jsonify({"code": 1001, "msg": "用户名或密码不能为空!!!"})

在这里,咱们设置的 token 是由 用户名+当前时间戳 拼接而成的,并把其放到redis中,能够到redis中查看token,或者在登陆成功后接口返回数据中的 login_info 中查看。而 token 的有效时间,这个是在配置文件 setting.py 中设置的 EXPIRE_TIME = 600 ,即 600 秒 后当前用户的 token 才会过时失效。app

删除用户请求接口实现

接下来,咱们准备新开发个接口:删除用户接口。该接口须要 管理员用户 登陆认证后才能够进行操做,管理员用户能够删除任何 普通用户 信息。

本来我是想经过 DELETE 请求方式来开发该接口,但好像使用 DELETE 方式的HTTP请求中,不容许传入Body,所以为了方便一些,咱们这里仍使用 POST 方式来请求。

  • 删除用户接口(POST请求方式)
@app.route("/delete/user/<int:id>", methods=['POST'])
def user_delete(id):
    username = request.json.get("username", "").strip()  # 当前登陆的管理员用户
    token = request.json.get("token", "").strip()  # token口令
    if username and token:
        redis_token = redis_db.handle_redis_token(username)  # 从redis中取token
        if redis_token:
            if redis_token == token:  # 若是从redis中取到的token不为空,且等于请求body中的token
                sql1 = "SELECT role FROM user WHERE username = '{}'".format(username)
                res1 = db.select_db(sql1)
                print("根据用户名 【 {} 】 查询到用户类型 == >> {}".format(username, res1))
                user_role = res1[0]["role"]
                if user_role == 0:  # 若是当前登陆用户是管理员用户
                    sql2 = "SELECT * FROM user WHERE id = '{}'".format(id)
                    res2 = db.select_db(sql2)
                    print("根据用户ID 【 {} 】 查询到用户信息 ==>> {}".format(id, res2))
                    if not res2:  # 若是要删除的用户不存在于数据库中,res2为空
                        return jsonify({"code": 3005, "msg": "删除的用户ID不存在,没法进行删除,请检查!!!"})
                    elif res2[0]["role"] == 0: # 若是要删除的用户是管理员用户,则不容许删除
                        return jsonify({"code": 3006, "msg": "用户ID:【 {} 】,该用户不容许删除!!!".format(id)})
                    else:
                        sql3 = "DELETE FROM user WHERE id = {}".format(id)
                        db.execute_db(sql3)
                        print("删除用户信息SQL ==>> {}".format(sql3))
                        return jsonify({"code": 0, "msg": "恭喜,删除用户信息成功!"})
                else:
                    return jsonify({"code": 3004, "msg": "当前用户不是管理员用户,没法进行操做,请检查!!!"})
            else:
                return jsonify({"code": 3003, "msg": "token口令不正确,请检查!!!"})
        else:
            return jsonify({"code": 3002, "msg": "当前用户未登陆,请检查!!!"})
    else:
        return jsonify({"code": 3001, "msg": "管理员用户/token口令不能为空,请检查!!!"})

相关的接口返回码和请求场景以下:

接口返回码 请求场景
0 请求参数正确,删除用户成功!
3001 请求参数中,管理员用户/token口令,任一参数为空
3002 请求参数中,当前操做用户没有token,登陆验证失败
3003 请求参数中的token值,与redis中的token值不一致
3004 请求参数中,当前操做用户不是管理员用户,无权限进行操做
3005 请求参数中,要删除的用户ID不存在
3006 请求参数中,要删除的用户是管理员用户,不容许删除

可参考以下进行接口请求:

请求方式:POST
请求地址:http://127.0.0.1:5000/delete/user/5
请求头:
Content-Type: application/json

Body:{"username": "wintest", "token": "wintest1587830406"}

接口请求示例

OK,经过以上操做,咱们已成功借助Redis来实现token登陆验证,固然,咱们redis的做用还有不少,在这里咱们只是简单的应用了一些,要学习的东西仍是不少。相关代码已上传到GitHub,你们有兴趣的能够基于此进行学习及开展接口测试。

GitHub源码地址:https://github.com/wintests/flaskDemo

相关文章
相关标签/搜索