在以前咱们已开发了几个接口,而且能够正常使用,那么今天咱们将继续完善一下。咱们注意到以前的接口,都是不须要进行任何验证就可使用的,其实咱们可使用 token
,好比设置在修改或删除用户信息的时候须要进行 token
登陆验证,这个地方咱们将引入 Redis
用于存储登陆时产生的 token
。python
本人环境:Python 3.7.0 、Redis 5.0.8git
咱们在项目根路径下 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
这个第三方库,具体安装方法以下: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:
咱们在项目根路径下 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,做为redis中的 key
和 value
,放到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 方式来请求。
@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