在以前開發的接口中,咱們設計把用戶信息存儲到數據庫時,沒有對數據進行加密處理,爲了提升下安全性,咱們今天就學習下,如何對用戶數據進行加密加鹽處理。git
MD5是經常使用的一種加密方法,它具備不可逆性,也就是說它只能加密,而不能進行解密,相對較安全。若是須要在Python3中使用MD5加密,直接使用內建模塊 hashlib
就好了,無需額外安裝。github
咱們以前設置的 password
密碼是明文的,好比 123456,這個是沒有進行加密的,若是使用MD5加密後是這樣的:redis
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加密,另外,由於咱們當前設計的用戶名是惟一且沒法修改的,因此能夠把用戶名也放進去進行加密處理,以提升用戶密碼的安全性。sql
咱們在項目根路徑下 config
包中,修改文件 setting.py
,在該文件中配置MD5加密加鹽處理的鹽值 SALT
,以下:數據庫
SALT = "test2020#%*"
同時,咱們在項目根路徑下 common
包中,新建文件 md5_operate.py
,該文件下存放了 MD5加密加鹽
的代碼,以下:json
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() # 返回密文
@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加密,而後把密文用於註冊並寫入到數據庫中。flask
@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
幾個字段的數據。app
這個接口是經過 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