##Flask 學習小組
> 我選擇使用國內的移動應用一站式云服務LeanCloud來實現短信驗證功能。LeanCloud 提供了數據存儲、實時消息、統計分析以及多種擴展組件,全面涵蓋移動應用開發的需求,支持 iOS、Android、Web 等多平臺。它幫助開發者擺脫后端開發負擔以專注于產品創新,縮短開發周期、節省開發投入。所以使用 LeanCloud 來實現我的短信驗證功能,簡化了我的開發流程、讓產品可以快速上線,提高效率。
### **功能實現流程:**

1. 客戶端發送請求給LeanCloud服務器,并在請求中包含該客戶端的手機號碼
1. LeanCloud服務器收到請求,生成相應驗證碼后,將該手機號碼的短信內容發送給運營商服務器
1. 運營商收到請求后,轉發帶有驗證碼的短信到該手機號
1. 客戶端收到短信驗證碼后,向LeanCloud發送驗證碼。
1. 此時,LeanCloud服務器檢查發送出的驗證碼和收到的驗證碼是否一致。
### **REST API**
> REST是Roy Thomas Fielding在他2000年的博士論文中提出的。他是HTTP協議(1.0版和1.1版)的主要設計者、Apache服務器軟件的作者之一。REST的全稱為Resource Representational State Transfer(資源在網絡中以某種表現形式進行狀態轉移),REST描述的是在網絡中client和server的一種交互形式。早期的網頁是前端與后端融合在一起的,比如PHP和JSP。但這不能滿足團隊的高效開發,也不能適應各種Client層的需求。REST可以通過統一的接口為Web、iOS和Android提供服務,對于我的網頁來說REST API 進行短信驗證便捷的同時也具有一定的可擴展性。
REST API 可以讓任何支持發送 HTTP 請求的設備與 LeanCloud 進行交互。使用 LeanCloud 的短信服務 REST API 可以完成很多事情,比如:
* 給指定手機號碼發送短信驗證碼
* 驗證手機號和短信驗證碼是否匹配
* 使用手機號和驗證碼進行登錄
* 通過合法的手機號和驗證碼來重置賬戶密碼
* 進行重要操作(例如支付)的驗證確認等
### **短信驗證API:**
**URL HTTP 功能**
/1.1/requestSmsCode POST 請求發送短信驗證碼
/1.1/verifySmsCode/(code) POST 驗證短信驗證碼
**requestSmsCode接口:**
給某個手機號碼發送驗證短信:(參考官方文檔)

概括為如下表格形式:
URL https://api.leancloud.cn/1.1/requestSmsCode
請求方式: POST
headers內容: X-LC-Id,X-LC-Key,Content-Type
data: JSON格式
其中data的內容為:
| 參數 | 約束 | 描述 |
| --- | --- | --- |
| mobilePhoneNumber | 必填 | 目標手機號碼 |
| ttl | | 驗證碼有效時間,單位分鐘(默認為 10 分鐘) |
| name | | 應用名字,默認為 LeanCloud 控制臺填寫的應用名 |
| op | | 操作類型 |
**verifySmsCode 接口**
通過下面的 API 可以驗證收到的 6 位數字驗證碼是否正確:(參考官方文檔)

概括為如下表格形式:
URL https://api.leancloud.cn/1.1/verifySmsCode/6位數字驗證碼?mobilePhoneNumber=186xxxxxxxx
請求方式 POST
headers內容 X-LC-Id,X-LC-Key,Content-Type
data 無
**1.注冊并創建應用**

每一個應用對應唯一的 App ID 和 App Key

**2.開啟短信驗證碼服務**

**3.后端代碼實現**
> 后端服務器需要實現的內容是根據瀏覽器的GET或POST請求做出響應。對于短信驗證來說后端只需要實現兩個功能。
* 獲取表單中電話號碼,發送 POST 請求給requestSmsCode API,請求給指定電話號碼發送驗證短信。
* 獲取表單中電話號碼與驗證碼發送給requestSmsCode API進行驗證。
~~~
sms.py :
import json
import requests
headers = {
"X-LC-Id": "1ltrHhRDe77M7FExy1RwqO78-gzGzoHsz",
"X-LC-Key": "A8gaYdOtGXRukMKTmcigwogE",
"Content-Type": "application/json",
}
REQUEST_SMS_CODE_URL= 'https://api.leancloud.cn/1.1/requestSmsCode'
VERIFY_SMS_CODE_URL = 'https://api.leancloud.cn/1.1/verifySmsCode/'
def send_message(phone):
data = {
"mobilePhoneNumber": phone,
}
r = requests.post(REQUEST_SMS_CODE_URL, data=json.dumps(data), headers=headers)
if r.status_code == 200:
return True
else:
return False
def verify(phone, code):
target_url = VERIFY_SMS_CODE_URL + "%s?mobilePhoneNumber=%s" % (code, phone)
r = requests.post(target_url, headers=headers)
if r.status_code == 200:
return True
else:
return False
~~~
**sms.py中包含兩個主要函數:**
1. send_message(phone):
給指定電話號碼發送驗證碼短信,data中包含了JSON格式的手機號碼,發送POST請求給requestSmsCode API,當HTTP狀態碼為200時代表發送成功,函數返回True,否則返回False。
1. verify(phone,code):
請求校驗驗證碼與手機號,發送POST請求給verifySmsCode API,當HTTP狀態碼為200時代表發送成功,函數返回True,否則返回False。
~~~
views.py:
@main.route('/signup', methods=('GET', 'POST'))
def signup():
if session['user_num']:
flash('You are already signed up')
return redirect(url_for('main.index'))
form = SignupForm()
if request.method == 'GET':
phone_number = request.args.get('mobile_phone_number')
if phone_number is not None:
if sms.send_message(phone_number):
return render_template('signup.html', form=form)
else:
flash('Failed to get verification code!')
elif request.method == 'POST':
if form.validate_on_submit():
user_num = User.query.filter_by(stu_num=form.stu_num.data).first()
if user_num is None:
phone_number = form.phone_number.data
code = form.code.data
if code == '':
flash('Please enter the verification code!')
elif sms.verify(phone_number, code):
name = form.name.data
stu_num = form.stu_num.data
password = form.password.data
user = User(name,stu_num,phone_number,password)
db.session.add(user)
db.session.commit()
session['user_num'] = stu_num
session['user_name'] = name
flash("Success to sign up!")
return redirect(url_for('main.index'))
else:
flash('Verification code error, please enter again')
return render_template('signup.html', form=form)
else:
flash("The student number has been registered!", 'error')
return render_template('signup.html', form=form)
else:
flash("Please enter the correct information.")
return render_template('signup.html', form=form)
return render_template('signup.html', form=form)
~~~
注:view.py已經導入了sms.py中的函數
在視圖函數中,我們先驗證用戶是否已經注冊,如果已經注冊不能再次進行注冊,將返回到主頁。創建用戶表單并判斷手機號碼一欄是否輸入為空,非空則判斷sms.send_message(phone_number)是否為真,為真則表示已經發送驗證碼,即可進入下一階段。之后判斷表單是否填寫完整,如有已注冊用戶將以flash的形式提示,否則進行驗證碼判斷,當sms.verify(phone_number, code)為真時代表驗證碼輸入正確,則將用戶輸入信息存入數據庫。
~~~
sign.html:
{% extends "base.html" %}
{% import "bootstrap/wtf.html" as wtf %}
{% block title %}注冊{% endblock %}
{% block head %}
{{ super() }}
<meta charset="UTF-8">
<script type="text/javascript">
var cur_count;
var count = 60;
var InterValObj;
var phone_number;
function send_message()
{
phone_number = document.getElementById("phone_number").value
if(phone_number)
{
cur_count = count;
document.getElementById("getCode").setAttribute("disabled", "true");
document.getElementById("getCode").value = "waiting for "+cur_count+"s";
InterValObj = window.setInterval(set_remain_time, 1000);
loadXMLDoc();
}
else
{
alert('Please input phone number!')
}
}
function set_remain_time()
{
if (cur_count == 0)
{
window.clearInterval(InterValObj);
document.getElementById("getCode").removeAttribute("disabled");
document.getElementById("getCode").value = "Get New Code";
}else
{
cur_count--;
document.getElementById("getCode").value = "waiting for "+cur_count+"s";
}
}
function loadXMLDoc()
{
var xmlhttp = new XMLHttpRequest();
xmlhttp.open("GET", "signup?mobile_phone_number=" + phone_number, true);
xmlhttp.send();
}
</script>
{% endblock %}
{% block page_content %}
<div class="page-header">
<h2>注冊</h2>
<form>
表單內容,與短信內容無關,暫時隱藏
</form>
</div>
{% endblock %}
~~~
前端的實現中使用到了簡單的Ajax異步處理和JavaScript的倒計時,AJAX= Asynchronous JavaScript and XML(異步的 JavaScript 和 XML)。AJAX不是新的編程語言,而是一種使用現有標準的新方法。AJAX 是在不重新加載整個頁面的情況下與服務器交換數據并更新部分網頁的技術。通過使用JavaScript可以讓頁面動態顯示再次發送驗證碼的剩余時間,時間到后可以點擊再次發送驗證碼,而這一過程并不需要重新加載頁面。
* * * * *
本人微信:sunyutong0725
歡迎交流