flask开发接口-Redis实现token验证及数据加密

919 阅读6分钟

认识Redis

什么是Redis

REmote DIctionary Server(Redis)是一个key-value存储系统,是跨平台的非关系型数据库。Redis 通常被称为数据结构服务器,因为值(value)可以是字符串(String)、哈希(Hash)、列表(list)、集合(sets)和有序集合(sorted sets)等类型。

安装Redis

下载地址:redis.io/download 找到最新稳定版本的版本号,输入命令进行下载安装。

# brew install wget
# wget http://download.redis.io/releases/redis-6.2.6.tar.gz
# tar xzf redis-6.2.6.tar.gz
# cd redis-6.2.6
# make

执行完make命令后,redis-6.2.6的src目录下会出现编译后的redis服务程序redis-server,还有用于测试的客户端程序redis-cli。
启动redis服务器:

# cd src
# ./redis-server

image.png 注意这种方式启动redis使用的是默认配置。也可以通过启动参数告诉redis使用指定配置文件使用下面命令启动。

# cd src
# ./redis-server ../redis.conf

redis.conf是一个默认的配置文件。我们可以根据需要使用自己的配置文件。 启动redis服务进程后,就可以使用测试客户端程序redis-cli和redis服务器交互了。

# cd src
# ./redis-cli
redis> set foo bar
OK
redis> get foo
"bar"

查看redis登陆密码:

# cd redis-6.2.6
# cd src
# ./redis-cli
# config get requirepass
# config set requirepass 123456 设置密码

Redis基础操作

redis远程服务器上操作redis

#连接远程redis服务器
redis-cli -h host -p port -a password

#查看所有满足pattern的key值
keys pattern

#查看key是否存在
exists key

#为给定 key 设置过期时间,以秒计
expire key seconds

#设置指定key的值(针对字符串)
set key value

#获取指定key的值(针对字符串)
get key

Python操作Redis

安装第三方库

pip install redis

封装Python操作Redis的代码

在登录前对请求参数中的明文密码进行MD5加密:md5_password = get_md5(username, password),然后再进行登录,而登录成功后,同样先对 token 进行MD5加密:token = get_md5(username, str(timeStamp)),再存储到redis中。

import redis
EXPIRE_TIME = 600

class RedisDb():
    def __init__(self, host, port, password):
        # 建立redis连接
	self.r = redis.Redis(
            host=host,
            port=port,
            password=password,
            decode_responses=True)

    def handle_redis_token(self, key, value = None):
        # 如果value非空,设置key和value,EXPIRE_TIME为过期时间
	if value:
            self.r.set(key, value, ex = EXPIRE_TIME)
        # 如果value为空,直接通过key从redis中取值
	else:
            redis_token = self.r.get(key)
            return redis_token

redis_db = RedisDb('127.0.0.1', 6379, "********")

在Redis中,我们存储数据是str字符串类型,但由于python3与redis交互的驱动问题,默认通过get()取出来的bytes字节类型。故在建立Redis连接时设置decode_responses=True,这样通过get()取出来就是str字符串类型。

登录时设置token

在成功登录后产生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:
	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)
            # 把token放到redis中存储
            redis_db.handle_redis_token(username, token)
            login_info = {
                "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":"Login Sucess!"})
    return jsonify({"code":1002, "msg":"Wrong username/password!"})
else:
    return jsonify({"code":1001, "msg":"Please input username/password!"})

设置的token由用户名+当前时间戳拼接而成,并把其放到redis中,可以到redis中查看token。而token的有效时间为设置的EXPIRE_TIME(600s),即600s后token过期失效。

删除用户接口(验证token)

该用户需要管理员用户登录认证后才可进行操作,管理员用户可以删除任何普通用户信息。

@app.route("/delete/user/<int:id>", methods=['POST'])
def user_delete(id):
    username = request.json.get("username", "").strip()
    token = request.json.get("token", "").strip()
    if username and token:
        # 从redis取token
	redis_token = redis_db.handle_redis_token(username)
	print(redis_token)
	if redis_token:
            # 如果从redis中取到的token不为空且等于请求boby中的token
            if redis_token == 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:
			return jsonify({"code":3005, "msg":"删除的用户ID不存在,无法进行删除,请检查!!!"})
                    # 如果要删除的用户为管理员,不允许被删除
                    elif res2[0]["role"] == 0:
			return jsonify({"code":3006, "meg":"用户ID:【{}】,该用户不允许删除!!!".format(id)})
                    else:
			sql3 = "DELETE FROM user WHERE id = {}".format(id)
			db.execute_db(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口令不能为空,请检查!!!"})

image.png

数据加密

为了提高安全性,可以对密码等机密数据进行加密加盐处理。

MD5加密

MD5是常用的一种加密方法,它具有不可逆性。即它只可加密不可解密,相对安全。在python3中使用MD5加密,直接使用内置模块hashlib即可。

加盐

加盐,是指通过对原始用户密码加一个复杂字符串后,然后再进行MD5加密,另外,因为我们当前设计的用户名是唯一且无法修改的,所以可以把用户名也放进去进行加密处理,以提高用户密码的安全性。

import hashlib
SALT= '2021test'

def get_md5(username, str):
	str = username + str + SALT
	md5 = hashlib.md5()
	md5.update(str.encode("utf-8"))
	return md5.hexdigest()

注册用户时MD5加密

只需把传过来的password通过MD5加密为密文即可,然后把密文存进数据库中

password = get_md5(username, password) # 把传入的明文密码通过MD5加密变为密文,然后再进行注册
sql3 = "INSERT INTO user(username, password, role, sex, telephone, address)VALUES('{}', '{}', '1', '{}', '{}', '{}')".format(username, password, sex, telephone, address)
db.execute_db(sql3)
print("新增用户信息SQL ==>> {}".format(sql3))
return jsonify({"code": 0, "msg": "恭喜,注册成功!"})

image.png

登录时使用MD5加密后再请求

md5_password = get_md5(username, password) # 把传入的明文密码通过MD5加密变为密文
sql2 = "SELECT * FROM user WHERE username = '{}' and password = '{}'".format(username, md5_password)
res2 = db.select_db(sql2)
print("获取 {} 用户信息 == >> {}".format(username, res2))
if res2:
    timeStamp = int(time.time()) # 获取当前时间戳
    # token = "{}{}".format(username, timeStamp)
    token = get_md5(username, str(timeStamp)) # MD5加密后得到token
    redis_db.handle_redis_token(username, token) # 把token放到redis中存储
    return_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": return_info, "msg": "恭喜,登录成功!"})