认识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
注意这种方式启动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口令不能为空,请检查!!!"})
数据加密
为了提高安全性,可以对密码等机密数据进行加密加盐处理。
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": "恭喜,注册成功!"})
登录时使用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": "恭喜,登录成功!"})