• 使用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

  • 相关阅读:
    结对项目之需求分析与原型设计
    第二次结对编程作业——毕设导师智能匹配
    历届软工作品、竞赛平台作品调研
    软件工程实践项目课程的自我目标
    Build to win!——获得小黄衫的感想
    VC++智能感知插件 Visual Assist X
    Haproxy+Keepalived高可用环境部署梳理(主主和主从模式)
    安装cronsun管理定时脚本
    四层、七层负载均衡的区别
    使用LVS实现负载均衡原理及安装配置详解
  • 原文地址:https://www.cnblogs.com/wintest/p/12780090.html
Copyright © 2020-2023  润新知