Flask專題之註冊介面開發
Flask的郵件註冊在《Flask Web開發》這本書中一節講解的很全面了,但由於本身從事是移動開發,移動開發中使用更多的簡訊驗證註冊的方式,所以百度參考了下別人的經驗,打算使用flask-redis外掛來實現驗證碼的快取和timeout失效機制。驗證碼服務商由於之前註冊過,為了方便起見暫時採用阿里大於的簡訊傳送。
下面按分析下如何去實現這個功能。
1、需求
從最簡單的角度分析,驗證碼註冊流程如下:
1. 提交電話號碼,傳送簡訊驗證碼
2. 驗證簡訊,存入密碼
2、實現
那我們依次來實現它們。
(1)阿里大於的sms傳送工具類
目前我看到阿里大於的python庫可以通過來安裝,以前都是需要手動匯入庫的。= 囧 =
pip install top
flask中整合,首先是App Key和App Secret的配置,由於只需要每次在啟動中呼叫一次,因此我將它放在了create_app方法中,在啟動時呼叫一次。
# ** coding:utf-8
import top
from flask import Flask
from flask_mongoengine import MongoEngine
from flask_redis import FlaskRedis
from config import config
mongo = MongoEngine()
redis_store = FlaskRedis()
aliServer = top.api.AlibabaAliqinFcSmsNumSendRequest()
def create_app(config_name):
app = Flask(__name__)
app.config.from_object(config[config_name])
mongo.init_app(app)
redis_store.init_app(app)
##阿里大於的配置註冊 ******中替換為自己的Key和Secret
top.setDefaultAppInfo("********", "********")
aliServer.set_app_info(top.getDefaultAppInfo())
from .auth import regist as regist_blueprint
app.register_blueprint(regist_blueprint, url_prefix='/auth')
return app
然後單獨寫一個sms工具類,所有的傳送簡訊操作我都封裝在裡面,後續只需要呼叫這個傳送方法即可。
## **coding:utf-8**
from app import aliServer
def send_sms(phone=None, validNum=None):
aliServer.extend = "123456"
aliServer.sms_param = "{\"code\": \"%s\" } " % str(validNum)
print aliServer.sms_param
aliServer.rec_num = str(phone)
##簡訊必須是normals
aliServer.sms_type = "normal"
aliServer.sms_free_sign_name = "極客M"
aliServer.sms_template_code = "SMS_68025025"
try:
resp = aliServer.getResponse()
print(resp)
return True, "傳送成功"
except Exception, e:
# errorcode=15 message=Remote service error subcode=isv.SMS_SIGNATURE_ILLEGAL submsg=簡訊簽名不合法 application_host=10.178.1.97 service_host=top010178001097.n.et2
print(e)
return False, e.submsg
(2)提交電話號碼,傳送驗證碼簡訊
1、 首先我們需要判斷該使用者是否已經存在,如果存在則直接返回使用者已存在的提示資訊。
@regist.route('/send_verify_sms', methods=['POST'])
def send_verify_sms():
phoneNumber = request.get_json().get('phone')
user = User.objects(phoneNumber=str(phoneNumber)).first()
if user:
return jsonify({'code': 0, 'message': '該使用者已經存在,註冊失敗'})
validate_number = str(random.randint(100000, 1000000))
print str(validate_number)
result, resMsg = send_sms(phoneNumber, validate_number)
return jsonify({'code': 1, 'message': resMsg})
2、 上面的功能雖然已經可以傳送驗證碼了,但有很多不完善的地方。因為簡訊服務商的服務中都對同一個手機號有每時 每日的最大發送限制數,且簡訊也是需要收費的,從節省成本和防止惡意註冊的角度考慮我們需要一套機制來限制使用者對這個介面的請求。
這裡我們採用和Redis資料庫結合起來。
請求時,先去查詢使用者呼叫傳送Api的次數。
# 驗證該使用者傳送次數
requestCount = redis_store.get('validateCount:%s' % phoneNumber)
if not requestCount:
requestCount = 0
else:
requestCount = int(requestCount)
if requestCount > 5:
return jsonify({'code': 0, 'message': '當日請求次數過多,請明日再試!'})
3、 檢視上一次傳送驗證碼的時間,如果驗證碼失效則重新發送新的驗證碼,如果沒有失效,則返回相應錯誤資訊。
#如果10min內有傳送過,則提示倒數時間,防止強制刪應用後進入的時間計數問題
ttlstime = redis_store.ttl('validateCode:%s' % phoneNumber)
if ttlstime > 0:
return jsonify({'code': 0, 'reulst': {'ttlstime': ttlstime}, 'message': '請等待 %d 秒後重試。' % ttlstime})
4、 傳送驗證碼成功後,向redis存入相應資料(該手機號的請求次數,該手機號的請求驗證碼有效期)
#傳送成功後 redis記錄相應資料
pipeline = redis_store.pipeline()
#請求次數記錄時間為1天
pipeline.set('validateCount:%s' % phoneNumber, int(requestCount) + 1)
pipeline.expire('validateCount:%s' % phoneNumber, 60 * 60 * 24)
#請求驗證碼的有效時間為10分鐘
pipeline.set('validateCode:%s' % phoneNumber, validate_number)
pipeline.expire('validateCode:%s' % phoneNumber, 60 * 10)
pipeline.execute()
5、將上述程式碼整合起來,我們的驗證碼傳送函式就算完成了。
@regist.route('/send_verify_sms', methods=['POST'])
def send_verify_sms():
phoneNumber = request.get_json().get('phone')
user = User.objects(phoneNumber=str(phoneNumber)).first()
if user:
return jsonify({'code': 0, 'message': '該使用者已經存在,註冊失敗'})
# 驗證該使用者傳送次數
requestCount = redis_store.get('validateCount:%s' % phoneNumber)
if not requestCount:
requestCount = 0
else:
requestCount = int(requestCount)
if requestCount > 5:
return jsonify({'code': 0, 'message': '當日請求次數過多,請明日再試!'})
#如果10min內有傳送過,則提示倒數時間,防止強制刪應用後進入的時間計數問題
ttlstime = redis_store.ttl('validateCode:%s' % phoneNumber)
if ttlstime > 0:
return jsonify({'code': 0, 'reulst': {'ttlstime': ttlstime}, 'message': '請等待 %d 秒後重試。' % ttlstime})
#傳送驗證碼
validate_number = str(random.randint(100000, 1000000))
result, resMsg = send_sms(phoneNumber, str(validate_number))
# 如果傳送不成功返回錯誤結果
if not result:
return jsonify({'code': 0, 'message': resMsg})
#傳送成功後 redis記錄相應資料
pipeline = redis_store.pipeline()
#請求次數記錄時間為1天
pipeline.set('validateCount:%s' % phoneNumber, int(requestCount) + 1)
pipeline.expire('validateCount:%s' % phoneNumber, 60 * 60 * 24)
#請求驗證碼的有效時間為10分鐘
pipeline.set('validateCode:%s' % phoneNumber, validate_number)
pipeline.expire('validateCode:%s' % phoneNumber, 60 * 10)
pipeline.execute()
return jsonify({'code': 1, 'message': resMsg})
(3)驗證碼的驗證與返回
首先我們去查詢該使用者是否請求過驗證碼:
validate_number_in_redis = redis_store.get('validateCode:%s' % phone_number)
if not validate_number_in_redis:
return jsonify({'code': 0, 'message': '該使用者沒有註冊,請先發送驗證碼!'})
然後驗證該驗證碼是否失效,此處我們根據之前設定的失效時間來判斷:
ttlstime = redis_store.ttl('validateCode:%s' % phone_number)
if ttlstime == -2:
return jsonify({'code': 0, 'reulst': {'ttlstime': ttlstime}, 'message': '驗證碼已失效'})
最後驗證,驗證碼是否匹配,不匹配則返回錯誤,匹配則返回成功:
if validate_number != validate_number_in_redis:
return jsonify({'code': 0, 'reulst': {'ttlstime': ttlstime}, 'message': '驗證失敗'})
user = User(phoneNumber=phone_number)
user.password=password
user.save()
return jsonify({'code': 1, 'reulst': {'token': ttlstime}, 'message': '註冊成功'})
綜上,整合下來,我們的註冊功能就算完成啦。