一、驗證碼
1.注冊藍圖
-
在info/modules文件夾下新建一個名字為passport的python文件夾,再在這個文件夾下新建一個views.py文件
- 打開passport/init.py文件
from flask import Blueprint
passport_blu = Blueprint("passport", __name__,url_prefix='/passport')
from . import views
- 打開info/init.py文件
from info.modules.passport import passport_blu
app.register_blueprint(passport_blu)

2.編寫驗證碼
- 打開static/news/js/mian.js(大概153行左右)
var imageCodeId = ""
var preimageCodeId = ""
// TODO 生成一個圖片驗證碼的編號,并設置頁面中圖片驗證碼img標簽的src屬性
function generateImageCode() {
//生成一個隨機字符串
imageCodeId = generateUUID();
//驗證碼圖片地址
image_url = '/passport/image_code?cur_id = '+imageCodeId+'&pre_id='+preimageCodeId
//將image_url設置到image標簽的src屬性中
$('.get_pic_code').attr('src',image_url)
//
preimageCodeId = imageCodeId
}

驗證碼字體文件包鏈接:https://pan.baidu.com/s/1ZDpaQEQw5DZ2mbZ9a5YS7w
提取碼:2mlx
- 在這個info文件夾下新建一個名為utils的文件夾,將上面的文件夾放入其中
- 這個字體記得導入到電腦字體庫中,其目錄為:C:\Windows\Fonts
- 安裝pillow
pip install pillow

response_code.py文件鏈接:https://pan.baidu.com/s/1vtCIxBNVG4s2nt0pCvhEBg
提取碼:fr5v
-
將上面這個文件放入到剛剛utils文件夾內(nèi)
- 打開info/modules/passport/views.py文件
#功能描述: 圖片驗證碼
#請求地址: /passport/image_code
#請求方式: GET
#請求參數(shù): 隨機字符串(uuid)cur_id, 上一個字符串:pre_id
#返回值: 返回圖片
from flask import jsonify, make_response, current_app, request
from info import redis_store, constants
from info.modules.passport import passport_blu
from info.utils.captcha.captcha import captcha
from info.utils.response_code import RET
@passport_blu.route('/image_code')
def get_image_code():
"""
思路分析:
1.獲取參數(shù)
2.校驗參數(shù)
3.生成圖片驗證碼
4.保存到redis
5.返回
:return:
"""
#1.獲取參數(shù)
cur_id = request.args.get('cur_id')
pre_id = request.args.get('pre_id')
#2.校驗參數(shù)
if not cur_id:
return jsonify(errno=RET.PARAMERR,errmsg='參數(shù)不全')
#3.生成圖片驗證碼
try:
name,text,image_data = captcha.generate_captcha()
#4.保存到redis
#參數(shù)1:保存到redis的key
#參數(shù)2:圖片驗證碼
#參數(shù)3:過期時間
redis_store.set('image_code:%s'%cur_id,text,constants.IMAGE_CODE_REDIS_EXPIRES)
#判斷有沒有上個編號
if pre_id:
redis_store.delete('image_code:%s'%pre_id)
except Exception as e:
current_app.logger.error(e)
return jsonify(errno=RET.DBERR,errmsg="驗證操作失敗")
#5.返回圖片驗證碼
response = make_response(image_data)
response.headers["Content-Type"] = 'image/jpg'
return response
-
刷新頁面
驗證碼出來了
-
打開redis數(shù)據(jù)庫
數(shù)據(jù)庫里面會出現(xiàn)驗證碼
二、短信驗證
1.云通訊注冊
- 我們短信驗證功能會使用第三方平臺,也就是云通訊
-
注冊賬號
云通訊包鏈接:https://pan.baidu.com/s/1iS9zc54G759NLnJg5N-37Q
提取碼:4idv
-
在info文件夾下新建一個libs文件夾,將剛剛的鏈接里面的文件夾放入libs中
-
打開sms.py文件
將這三個數(shù)值進行一一對應
- 運行程序
在運行程序的時候我這邊出現(xiàn)了一個問題,出現(xiàn){'172001':'網(wǎng)絡錯誤'}
我的解決辦法是放入了兩行代碼:
import ssl
ssl._create_default_https_context = ssl._create_unverified_context # 全局取消證書驗證
# 說明:請求地址,生產(chǎn)環(huán)境配置成cloopen.com
_serverIP = 'app.cloopen.com'
再次運行的話就會好使了

- 打開passport/views.py文件
#功能描述: 發(fā)送短信
# 請求路徑: /passport/sms_code
# 請求方式: POST
# 請求參數(shù): mobile, image_code,image_code_id
# 返回值: errno, errmsg
@passport_blu.route('/sms_code',methods=['POST'])
def get_sms_code():
"""
思路分析:
1.獲取參數(shù)
2.校驗參數(shù),為空檢驗,格式校驗
3.取出redis中的圖片驗證碼
4.判斷是否過期
5.刪除redis中圖片驗證碼
6.正確性判斷
7.生成短信驗證碼
8.發(fā)送短信
9.判斷是否發(fā)送成功
10.保存短信驗證碼到redis
11.返回響應
"""
# 1.獲取參數(shù)
json_data = request.data
dict_data = json.loads(json_data)
mobile = dict_data.get('mobile')
image_code = dict_data.get('image_code')
image_code_id = dict_data.get('image_code_id')
# 2.校驗參數(shù),為空檢驗,格式校驗
if not all([mobile,image_code,image_code_id]):
return jsonify(errno=RET.PARAMERR,errmsg="參數(shù)不全")
if not re.match('1[35789]\d{9}',mobile):
return jsonify(errno=RET.DATAERR,errmsg="手機號格式不正確")
# 3.取出redis中的圖片驗證碼
try:
redis_image_code = redis_store.get('image_code:%s'%image_code_id)
except Exception as e:
current_app.logger.error(e)
return jsonify(errno=RET.DBERR,errmsg="數(shù)據(jù)獲取失敗")
# 4.判斷是否過期
if not redis_image_code:
return jsonify(errno=RET.NODATA,errmsg="圖片驗證碼過期")
# 5.刪除redis中圖片驗證碼
try:
redis_store.delete('image_code:%s'%image_code_id)
except Exception as e:
current_app.logger.error(e)
return jsonify(errno=RET.DBERR,errmsg="獲取失敗")
# 6.正確性判斷
if image_code.upper() != redis_image_code.upper():
return jsonify(errno=RET.DATAERR,errmsg="圖片驗證碼錯誤")
# # 7.生成短信驗證碼
sms_code = '%06d'%random.randint(0,999999)
current_app.logger.debug('短信驗證碼 = %s'%sms_code )
# 8.發(fā)送短信
try:
ccp = CCP()
result = ccp.send_template_sms(mobile,[sms_code,constants.SMS_CODE_REDIS_EXPIRES/60],1)
except Exception as e:
current_app.logger.error(e)
return jsonify(errno=RET.THIRDERR,errmsg="云通訊發(fā)送失敗")
# 9.判斷是否發(fā)送成功
if result == -1:
return jsonify(errno=RET.DATAERR,errmsg="發(fā)送短信失敗")
# 10.保存短信驗證碼到redis
try:
redis_store.set('sms_code:%s'%mobile,sms_code,constants.SMS_CODE_REDIS_EXPIRES)
except Exception as e:
current_app.logger.error(e)
return jsonify(errno=RET.DBERR,errmsg="短信保存失敗")
# 11.返回響應
return jsonify(errno=RET.OK,errmsg="發(fā)送成功")
- 打開info/static/news/js/main.js文件
// TODO 發(fā)送短信驗證碼
//拼接參數(shù)
var params = {
"mobile": mobile,
"image_code": imageCode,
"image_code_id": imageCodeId
}
//發(fā)送獲取短信請求
$.ajax({
url: '/passport/sms_code',//請求地址
type: 'post',
data: JSON.stringify(params),
contentType: 'application/json',
headers: {'X-CSRFToken': getCookie('csrf_token')},
success: function (resp) {
//判斷是否請求成功
if (resp.errno == '0') {
//定義倒計時時間
var num = 60;
//創(chuàng)建定時器
var t = setInterval(function () {
//判斷是否倒計時結(jié)束
if (num == 1) {
//清除定時器
clearInterval(t)
//設置標簽點擊事件,并設置內(nèi)容
$(".get_code").attr("onclick", 'sendSMSCode()');
$(".get_code").html('點擊獲取驗證碼');
} else {
//設置秒數(shù)
num -= 1;
$('.get_code').html(num + '秒');
}
}, 1000);//一秒走一次
} else {//發(fā)送失敗
alert(resp.errmsg);
// 重新設置點擊事件,更新圖片驗證碼
$(".get_code").attr("onclick", 'sendSMSCode()');
generateImageCode();
}
}
})
}
-
運行程序
手機上也收到了短信驗證碼
三、注冊與登錄
- 打開passport/views.py文件
# 登錄
@passport_blu.route('/login', methods=['POST'])
def login():
# 1.獲取參數(shù)
# 2.校驗
# 3.通過手機號獲取對象
# 4. 判斷用戶是否存在
# 5.判斷密碼
# 6. 保存用戶信息到session中
# 7. 返回響應
# 1.獲取參數(shù)
mobile = request.json.get('mobile')
password = request.json.get('password')
# 2.校驗
if not all([mobile, password]):
return jsonify(errno=RET.PARAMERR, errmsg="參數(shù)不全")
if not re.match('1[35789]\d{9}',mobile):
return jsonify(errno=RET.DATAERR,errmsg="手機號格式不正確")
# 3.通過手機號獲取對象
try:
user = User.query.filter(User.mobile == mobile).first()
except Exception as e:
current_app.logger.error(e)
return jsonify(errno=RET.DBERR,errmsg="查詢用戶異常")
# 4. 判斷用戶是否存在
if not user:
return jsonify(errno=RET.NODATA,errmsg="該用戶未注冊")
# 5.判斷密碼
# if not user.check_passowrd(password):
if user.password_hash != password:
return jsonify(errno=RET.DATAERR,errmsg="密碼錯誤")
# 6. 保存用戶信息到session中
session['user_id'] = user.id
session['mobile'] = user.mobile
session['nick_name'] = user.nick_name
user.last_login = datetime.now()
# 7. 返回響應
current_app.logger.debug('登錄成功')
return jsonify(errno=RET.OK, errmsg="登錄成功")
# 注冊功能的實現(xiàn)
@passport_blu.route('/register', methods=['POST'])
def register():
"""
1. 獲取參數(shù)
2.校驗參數(shù)
3. 通過手機號取出驗證碼
4. 判斷短信驗證碼是否過期
5. 刪除redis中的短信驗證碼
6.判斷驗證碼的正確性
7.創(chuàng)建用戶對象
8.設置用戶屬性
9.保存到數(shù)據(jù)庫
10. 返回響應
:return:
"""
# json_data = request.data
# dict_data = json.loads(json_data)
# 下面這句話等同于上面兩句
dict_data = request.json
mobile = dict_data.get('mobile')
sms_code = dict_data.get('sms_code')
password = dict_data.get('password')
# 2.校驗參數(shù)
if not all([mobile,sms_code,password]):
return jsonify(errno=RET.PARAMERR,errmsg="參數(shù)不全")
# 3. 通過手機號取出驗證碼
try:
redis_sms_code = redis_store.get('sms_code:%s'%mobile)
except Exception as e:
current_app.logger.error(e)
return jsonify(errno=RET.DBERR,errmsg="獲取短信驗證碼異常")
#判斷短信驗證碼是否過期
if not redis_sms_code:
return jsonify(errno=RET.NODATA,errmsg="短信驗證碼已經(jīng)過期")
# 5. 刪除redis中的短信驗證碼
try:
redis_store.delete('sms_code:%s' % mobile)
except Exception as e:
current_app.logger.error(e)
return jsonify(errno=RET.DBERR, errmsg="刪除短信驗證碼異常")
# 6.判斷驗證碼的正確性
if redis_sms_code != sms_code:
return jsonify(errno=RET.DATAERR, errmsg="短信驗證碼錯誤")
# 7.創(chuàng)建用戶對象
user = User()
# 8.設置用戶屬性
user.nick_name = mobile
user.password = password
user.mobile = mobile
# 9.保存到數(shù)據(jù)庫
try:
db.session.add(user)
db.session.commit()
except Exception as e:
current_app.logger.error(e)
db.session.rollback()
return jsonify(errno=RET.DBERR, errmsg="用戶注冊失敗")
# 10. 返回響應
return jsonify(errno=RET.OK, errmsg="用戶注冊成功")
- 打開info/static/news/js/main.js文件
// TODO 登錄表單提交
$(".login_form_con").submit(function (e) {
// 阻止默認提交操作, 不讓其往默認的action提交
e.preventDefault()
// 取到用戶輸入的內(nèi)容
var mobile = $(".login_form #mobile").val()
var password = $(".login_form #password").val()
// 判斷是否為空
if (!mobile) {
$("#login-mobile-err").show();
return;
}
if (!password) {
$("#login-password-err").show();
return;
}
// 發(fā)起注冊請求
//拼接參數(shù)
var params = {
"mobile": mobile,
"password": password
}
$.ajax({
url: '/passport/login',//請求地址
type: 'post',
data: JSON.stringify(params),
contentType: 'application/json',
headers: {'X-CSRFToken': getCookie('csrf_token')},
success: function (resp) {
//判斷是否請求成功
if (resp.errno == '0') {
// 重新加載當前頁面即可
alert("登錄成功")
window.location.reload()
} else {//發(fā)送失敗
alert(resp.errmsg);
}
}
})
})
// TODO 注冊按鈕點擊
$(".register_form_con").submit(function (e) {
// 阻止默認提交操作, 不讓其往默認的action提交
e.preventDefault()
// 取到用戶輸入的內(nèi)容
var mobile = $("#register_mobile").val()
var sms_code = $("#smscode").val()
var password = $("#register_password").val()
// 判斷是否為空
if (!mobile) {
$("#register-mobile-err").show();
return;
}
if (!sms_code) {
$("#register-sms-code-err").show();
return;
}
if (!password) {
$("#register-password-err").html("請?zhí)顚懨艽a");
$("#register-password-err").show();
return;
}
if (password.length < 6) {
$("#register-password-err").html("密碼長度不能少于6位");
$("#register-password-err").show();
return;
}
// 發(fā)起注冊請求
//拼接參數(shù)
var params = {
"mobile": mobile,
"sms_code": sms_code,
"password": password
}
$.ajax({
url: '/passport/register',//請求地址
type: 'post',
data: JSON.stringify(params),
contentType: 'application/json',
headers: {'X-CSRFToken': getCookie('csrf_token')},
success: function (resp) {
//判斷是否請求成功
if (resp.errno == '0') {
// 重新加載當前頁面即可
alert("注冊成功")
window.location.reload()
} else {//發(fā)送失敗
alert(resp.errmsg);
}
}
})
})
})
-
運行程序
-
我們將數(shù)據(jù)庫的passwor_hash里面的數(shù)據(jù)改成我們設的密碼
-
運行程序
-
打開redis數(shù)據(jù)庫,查看數(shù)據(jù)
以上項目可在我的GitHub上面查看:
(此文章僅作為個人學習筆記使用,如有錯誤歡迎指正~)















