zoukankan      html  css  js  c++  java
  • 使用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

  • 相关阅读:
    linux权限补充:rwt rwT rws rwS 特殊权限
    关于Linux操作系统下文件特殊权限的解释
    Java学习笔记——Java程序运行超时后退出或进行其他操作的实现
    Java实现 蓝桥杯 算法提高 判断名次
    Java实现 蓝桥杯 算法提高 判断名次
    Java实现 蓝桥杯 算法提高 日期计算
    Java实现 蓝桥杯 算法提高 日期计算
    Java实现 蓝桥杯 算法提高 概率计算
    Java实现 蓝桥杯 算法提高 概率计算
    Java实现 蓝桥杯 算法提高 复数四则运算
  • 原文地址:https://www.cnblogs.com/wintest/p/12780090.html
Copyright © 2011-2022 走看看