240
生活,简单就好!

使用Flask开发简单接口(5)--数据加密处理

前言

在之前开发的接口中,我们设计把用户信息存储到数据库时,没有对数据进行加密处理,为了提高下安全性,我们今天就学习下,如何对用户数据进行加密加盐处理。

MD5加密加盐

  • MD5加密

MD5是常用的一种加密方法,它具有不可逆性,也就是说它只能加密,而不能进行解密,相对较安全。如果需要在Python3中使用MD5加密,直接使用内建模块 hashlib 就行了,无需额外安装。

我们之前设置的 password 密码是明文的,比如 123456,这个是没有进行加密的,如果使用MD5加密后是这样的:

import hashlib

def get_md5(str):
    """MD5加密处理"""
    md5 = hashlib.md5()  # 创建md5对象
    md5.update(str.encode("utf-8"))  # Python3中需要先转换为 bytes 类型,才能加密
    return md5.hexdigest() # 返回密文

password = "123456"
print("MD5加密前:{}".format(password)) # 123456
md5_pwd = get_md5((password))
print("MD5加密后:{}".format(md5_pwd)) # e10adc3949ba59abbe56e057f20f883e
  • 加盐

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

我们在项目根路径下 config 包中,修改文件 setting.py ,在该文件中配置MD5加密加盐处理的盐值 SALT,如下:

SALT = "test2020#%*"

同时,我们在项目根路径下 common 包中,新建文件 md5_operate.py ,该文件下存放了 MD5加密加盐 的代码,如下:

import hashlib
from config.setting import MD5_SALT

def get_md5(username, str):
    """MD5加密处理"""
    str = username + str + MD5_SALT  # 把用户名也作为str加密的一部分
    md5 = hashlib.md5()  # 创建md5对象
    md5.update(str.encode("utf-8"))  # Python3中需要先转换为 bytes 类型,才能加密
    return md5.hexdigest()  # 返回密文

注册用户时MD5加密

@app.route("/register", methods=['POST'])
def user_register():
    """注册用户"""
    username = request.json.get("username", "").strip()  # 用户名
    password = request.json.get("password", "").strip()  # 密码
    sex = request.json.get("sex", "0").strip()  # 性别,默认为0(男性)
    telephone = request.json.get("telephone", "").strip()  # 手机号
    address = request.json.get("address", "").strip()  # 地址,默认为空串
    if username and password and telephone: # 注意if条件中 "" 也是空, 按False处理
        sql1 = "SELECT username FROM user WHERE username = '{}'".format(username)
        res1 = db.select_db(sql1)
        print("查询到用户名 ==>> {}".format(res1))
        sql2 = "SELECT telephone FROM user WHERE telephone = '{}'".format(telephone)
        res2 = db.select_db(sql2)
        print("查询到手机号 ==>> {}".format(res2))
        if res1:
            return jsonify({"code": 2002, "msg": "用户名已存在,注册失败!!!"})
        elif not (sex == "0" or sex == "1"):
            return jsonify({"code": 2003, "msg": "输入的性别只能是 0(男) 或 1(女)!!!"})
        elif not (len(telephone) == 11 and re.match("^1[3,5,7,8]\d{9}$", telephone)):
            return jsonify({"code": 2004, "msg": "手机号格式不正确!!!"})
        elif res2:
            return jsonify({"code": 2005, "msg": "手机号已被注册!!!"})
        else:
            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": "恭喜,注册成功!"})
    else:
        return jsonify({"code": 2001, "msg": "用户名/密码/手机号不能为空,请检查!!!"})

在上面代码中,我们只在注册之前增加了一行代码:password = get_md5(username, password),先把请求参数中传入的明文密码进行MD5加密,然后把密文用于注册并写入到数据库中。

登录用户时MD5加密

@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": "用户名不存在!!!"})
        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": "恭喜,登录成功!"})
        return jsonify({"code": 1002, "msg": "用户名或密码错误!!!"})
    else:
        return jsonify({"code": 1001, "msg": "用户名或密码不能为空!!!"})

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

修改用户请求接口实现

接下来,我们准备新开发个接口:修改用户接口。该接口需要 管理员用户 登录认证后才可以进行操作,管理员用户可以修改任何用户信息。但在修改用户信息时,只允许修改 密码 password、性别 sex、手机号 telephone、联系地址 address 几个字段的数据。

  • 修改用户接口(PUT接口)

这个接口是通过 PUT 方式来进行请求,在 Flask 中,如果要让请求接口接口支持 PUT 请求方式,我们只需要在 methods 中设置就行。

@app.route("/update/user/<int:id>", methods=['PUT'])
def user_update(id): # id为准备修改的用户ID
    """修改用户信息"""
    username = request.json.get("username", "").strip() # 当前登录的管理员用户
    token = request.json.get("token", "").strip()  # token口令
    new_password = request.json.get("password", "").strip()  # 新的密码
    new_sex = request.json.get("sex", "0").strip()  # 新的性别,如果参数不传sex,那么默认为0(男性)
    new_telephone = request.json.get("telephone", "").strip()  # 新的手机号
    new_address = request.json.get("address", "").strip()  # 新的联系地址,默认为空串
    if username and token and new_password and new_telephone: # 注意if条件中空串 "" 也是空, 按False处理
        if not (new_sex == "0" or new_sex == "1"):
            return jsonify({"code": 4007, "msg": "输入的性别只能是 0(男) 或 1(女)!!!"})
        elif not (len(new_telephone) == 11 and re.match("^1[3,5,7,8]\d{9}$", new_telephone)):
            return jsonify({"code": 4008, "msg": "手机号格式不正确!!!"})
        else:
            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))
                        sql3 = "SELECT telephone FROM user WHERE telephone = '{}'".format(new_telephone)
                        res3 = db.select_db(sql3)
                        print("查询到手机号 ==>> {}".format(res3))
                        if not res2: # 如果要修改的用户不存在于数据库中,res2为空
                            return jsonify({"code": 4005, "msg": "修改的用户ID不存在,无法进行修改,请检查!!!"})
                        elif res3: # 如果要修改的手机号已经存在于数据库中,res3非空
                            return jsonify({"code": 4006, "msg": "手机号已被注册,无法进行修改,请检查!!!"})
                        else:
                            # 如果请求参数不传address,那么address字段不会被修改,仍为原值
                            if not new_address:
                                new_address = res2[0]["address"]
                            # 把传入的明文密码通过MD5加密变为密文
                            new_password = get_md5(res2[0]["username"], new_password)
                            sql3 = "UPDATE user SET password = '{}', sex = '{}', telephone = '{}', address = '{}' " \
                                   "WHERE id = {}".format(new_password, new_sex, new_telephone, new_address, id)
                            db.execute_db(sql3)
                            print("修改用户信息SQL ==>> {}".format(sql3))
                            return jsonify({"code": 0, "msg": "恭喜,修改用户信息成功!"})
                    else:
                        return jsonify({"code": 4004, "msg": "当前用户不是管理员用户,无法进行操作,请检查!!!"})
                else:
                    return jsonify({"code": 4003, "msg": "token口令不正确,请检查!!!"})
            else:
                return jsonify({"code": 4002, "msg": "当前用户未登录,请检查!!!"})
    else:
        return jsonify({"code": 4001, "msg": "管理员用户/token口令/密码/手机号不能为空,请检查!!!"})

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

接口返回码 请求场景
0 请求参数正确,修改用户信息成功!
4001 请求参数中,管理员用户/token口令/密码/手机号,任一参数为空
4002 请求参数中,当前操作用户没有token,登录验证失败
4003 请求参数中的token值,与redis中的token值不一致
4004 请求参数中,当前操作用户不是管理员用户,无权限进行操作
4005 请求参数中,要删除的用户ID不存在
4006 请求参数中,手机号已被其他人注册使用
4007 请求参数中, sex 性别字段值不是 0 或 1
4008 请求参数中,手机号格式不正确

可参考如下进行修改用户接口请求( token 可以从用户登录成功后的接口返回数据中获取):

请求方式:PUT
请求地址:http://127.0.0.1:5000/update/user/3
请求头:
Content-Type: application/json

Body:{"username": "wintest", "token": "f54f9d6ebba2c75d45ba00a8832cb593", "sex": "1", "address": "广州市天河区", "password": "12345678", "telephone": "13500010003"}

接口请求示例

OK,通过以上操作,我们已成功对用户密码和token串进行了数据加密处理,并实现了修改用户的功能,相关代码已上传到GitHub,大家有兴趣的可以基于此进行学习及开展接口测试。

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

posted @ 2020-04-26 15:47  wintest  阅读(2195)  评论(0编辑  收藏  举报