flask開發restful api系列(5)-短信驗證碼

  咱們如今開發app,註冊用戶的時候,再也不像web同樣,發送到我的郵箱了,畢竟我的郵箱在移動端填寫驗證都很麻煩,通常都採用短信驗證碼的方式。今天咱們就講講這方面的內容。web

  首先,先找一個平臺吧。咱們公司找的容聯雲通信這個平臺,至少目前爲止,用的還能夠。先在容聯上註冊一下,而後建立一個應用,以下圖所示:redis

  我只勾選了2個功能,他們這邊還有不少其餘功能,暫時用不到,就不選了。好了,點擊"確認",一個應用就弄好了,下面就嘗試着寫代碼發短信吧。json

  容聯爲開發者提供了免費測試功能,但一個號碼基本不會超過3次,因此用的時候要當心哦!容聯的文檔可能寫的有點複雜了,反正我以爲稍微有點複雜,其實就是post一個request,咱們把它提煉一下,獲得下面這些代碼。api

 1 # coding:utf-8
 2 import datetime
 3 import hashlib
 4 import requests
 5 import json
 6 import base64
 7 
 8 
 9 def message_validate(phone_number, validate_number):
10     accountSid = "×××××××××"
11     accountToken = "×××××××××"
12     appid = "××××××××××"
13     templateId = '1'
14     now = datetime.datetime.now().strftime("%Y%m%d%H%M%S")
15     signature = accountSid + accountToken + now
16     m = hashlib.md5()
17     m.update(signature)
18     sigParameter = m.hexdigest().upper()
19     # sigParameter = hashlib.md5().update(signature).hexdigest().upper()
20     url = "https://sandboxapp.cloopen.com:8883/2013-12-26/Accounts/%s/SMS/TemplateSMS?sig=%s" % (accountSid, sigParameter)
21     authorization = accountSid + ':' + now
22     new_authorization = base64.encodestring(authorization).strip()
23     headers = {'content-type': 'application/json;charset=utf-8', 'accept': 'application/json',
24                'Authorization': new_authorization}
25     data = {'to': phone_number, 'appId': appid, 'templateId': templateId, 'datas': [str(validate_number), '3']}
26     response = requests.post(url=url, data=json.dumps(data), headers=headers)
27     if response.json()['statusCode'] == '000000':
28         return True, response.json().get('statusMsg')
29     else:
30         return False, response.json().get('statusMsg')
31 
32 if __name__ == '__main__':
33     result, reason = message_validate('137×××××××', '123456')
34     if result:
35         print '發送成功'
36     else:
37         print '發送失敗'
38         print '緣由是:' + reason.encode('utf-8')

 看,這就是從新編輯的函數,是否是很簡單,逐行分析一下吧。session

首先, accountSid和accountToken 其實就至關於帳戶的用戶名和密碼,主要在這個頁面能夠看到。app

其次,appid就是應用的appid,直接填進去就能夠dom

至於templateId,其實就是你添加新的模板的id號,咱們這邊用開發者帳號,直接填寫'1'就能夠了。函數

好了, 下面的代碼,只要熟悉http的人,都會很是熟悉,基本都是把帳號的id和token加上時間戳,轉換成md5值,而後再encode一下,變成http的基本驗證。判別有沒有成功,按官網返回的參數,直接解析一下,是'000000'就表明成功,不然失敗。oop

是否是很簡單?就是這麼簡單,好了,既然發送短信的函數寫好了,下面就寫註冊api接口吧。post

通常的移動註冊api接口能夠分爲3步

一、提交電話號碼,發送短信驗證,

二、驗證短信

三、密碼提交,

四、基本資料提交

一共4個接口,就register 1 2 3 4 吧,具體代碼以下:

  1 @app.route('/register-step-1', methods=['POST'])
  2 def register_step_1():
  3     """
  4     接受phone_number,發送短信
  5     """
  6     phone_number = request.get_json().get('phone_number')
  7     user = User.query.filter_by(phone_number=phone_number).first()
  8 
  9     if user:
 10         return jsonify({'code': 0, 'message': '該用戶已經存在,註冊失敗'})
 11     validate_number = str(random.randint(100000, 1000000))
 12     result, err_message = message_validate(phone_number, validate_number)
 13 
 14     if not result:
 15         return jsonify({'code': 0, 'message': err_message})
 16 
 17     pipeline = redis_store.pipeline()
 18     pipeline.set('validate:%s' % phone_number, validate_number)
 19     pipeline.expire('validate:%s' % phone_number, 60)
 20     pipeline.execute()
 21 
 22     return jsonify({'code': 1, 'message': '發送成功'})
 23 
 24 
 25 @app.route('/register-step-2', methods=['POST'])
 26 def register_step_2():
 27     """
 28     驗證短信接口
 29     """
 30     phone_number = request.get_json().get('phone_number')
 31     validate_number = request.get_json().get('validate_number')
 32     validate_number_in_redis = redis_store.get('validate:%s' % phone_number)
 33 
 34     if validate_number != validate_number_in_redis:
 35         return jsonify({'code': 0, 'message': '驗證沒有經過'})
 36 
 37     pipe_line = redis_store.pipeline()
 38     pipe_line.set('is_validate:%s' % phone_number, '1')
 39     pipe_line.expire('is_validate:%s' % phone_number, 120)
 40     pipe_line.execute()
 41 
 42     return jsonify({'code': 1, 'message': '短信驗證經過'})
 43 
 44 
 45 @app.route('/register-step-3', methods=['POST'])
 46 def register_step_3():
 47     """
 48     密碼提交
 49     """
 50     phone_number = request.get_json().get('phone_number')
 51     password = request.get_json().get('password')
 52     password_confirm = request.get_json().get('password_confirm')
 53 
 54     if len(password) < 7 or len(password) > 30:
 55         # 這邊能夠本身拓展條件
 56         return jsonify({'code': 0, 'message': '密碼長度不符合要求'})
 57 
 58     if password != password_confirm:
 59         return jsonify({'code': 0, 'message': '密碼和密碼確認不一致'})
 60 
 61     is_validate = redis_store.get('is_validate:%s' % phone_number)
 62 
 63     if is_validate != '1':
 64         return jsonify({'code': 0, 'message': '驗證碼沒有經過'})
 65 
 66     pipeline = redis_store.pipeline()
 67     pipeline.hset('register:%s' % phone_number, 'password', password)
 68     pipeline.expire('register:%s' % phone_number, 120)
 69     pipeline.execute()
 70 
 71     return jsonify({'code': 1, 'message': '提交密碼成功'})
 72 
 73 
 74 @app.route('/register-step-4', methods=['POST'])
 75 def register_step_4():
 76     """
 77     基本資料提交
 78     """
 79     phone_number = request.get_json().get('phone_number')
 80     nickname = request.get_json().get('nickname')
 81 
 82     is_validate = redis_store.get('is_validate:%s' % phone_number)
 83 
 84     if is_validate != '1':
 85         return jsonify({'code': 0, 'message': '驗證碼沒有經過'})
 86 
 87     password = redis_store.hget('register:%s' % phone_number, 'password')
 88 
 89     new_user = User(phone_number=phone_number, password=password, nickname=nickname)
 90     db_session.add(new_user)
 91 
 92     try:
 93         db_session.commit()
 94     except Exception as e:
 95         print e
 96         db_session.rollback()
 97         return jsonify({'code': 0, 'message': '註冊失敗'})
 98     finally:
 99         redis_store.delete('is_validate:%s' % phone_number)
100         redis_store.delete('register:%s' % phone_number)
101 
102     return jsonify({'code': 1, 'message': '註冊成功'})

 

看到上面register的4個步驟沒有,這邊要注意的是具體方法:

步驟1:提交手機號碼,驗證。這個很基礎,就不用說了,重要的是,發送太短信以後,要把短信驗證碼存在redis裏面,以便下一個接口調用;其次,這個存儲過程,必定要用pipeline,還要設置一個超時刪除。想想,假設你的程序在註冊的過程當中,崩掉,或者你中斷程序,最起碼不要影響其餘程序,若是沒有超時值,會產生不少的垃圾值,而且你還很難注意到。

步驟2:從redis裏找到以前存儲的驗證碼,對比,成功就進入下一步。這邊,我還設置了一個is_validate值,最主要是防止客戶端同事在這步會出錯,或者其餘知道這個接口的人,直接用腳本訪問後面的接口,這樣會出現未知的錯誤。

步驟3:驗證一下密碼是否符合要求,而後看一下上一步設置的is_validate是否存在,上面說了,防止惡意用戶直接訪問下面的接口,而後保存password到一個redis的hash值。這邊主要爲了方便客戶端同事,否則下一個接口還要從新上傳password值,客戶端同事必定會惱火的。

步驟4:提交基本資料,而後保存。這邊最重要的是,無論註冊成功失敗,本身注意把redis裏面的值清理乾淨。看看我上面的接口,全部這些臨時註冊值,都設置了一個超時值,超過期間,就清理掉。

整個過程就完成了,能夠去驗證一下結果了。(其實這裏還有缺陷,假設在第二步,我知道你這個接口,寫個小腳本,暴力破解你的驗證碼,很快就能拿到的。這邊能夠作個小改動,在redis裏面加一個值,訪問一次,則添加1,超過必定次數,就返回錯誤代碼。很簡單,這邊就不深刻了)

好了,客戶端驗證代碼以下:

 1 # coding:utf-8
 2 import requests
 3 import json
 4 from qiniu import put_file
 5 
 6 
 7 class APITest(object):
 8     def __init__(self, base_url):
 9         self.base_url = base_url
10         self.headers = {}
11         self.token = None
12         self.qiniu_token = None
13         self.qiniu_key = None
14         self.qiniu_base_url = 'http://7xk6rc.com1.z0.glb.clouddn.com/'
15 
16     def login(self, phone_number, password, path='/login'):
17         payload = {'phone_number': phone_number, 'password': password}
18         self.headers = {'content-type': 'application/json'}
19         response = requests.post(url=self.base_url + path, data=json.dumps(payload), headers=self.headers)
20         response_data = json.loads(response.content)
21         self.token = response_data.get('token')
22         return response_data
23 
24     def user(self, path='/user'):
25         self.headers = {'token': self.token}
26         response = requests.get(url=self.base_url + path, headers=self.headers)
27         response_data = json.loads(response.content)
28         return response_data
29 
30     def logout(self, path='/logout'):
31         self.headers = {'token': self.token}
32         response = requests.get(url=self.base_url + path, headers=self.headers)
33         response_data = json.loads(response.content)
34         return response_data
35 
36     def get_qiniu_token(self, path='/get-qiniu-token'):
37         response = requests.get(url=self.base_url + path)
38         response_data = json.loads(response.content)
39         self.qiniu_token = response_data.get('token')
40         self.qiniu_key = response_data.get('key')
41         if self.qiniu_token and self.qiniu_key:
42             print '成功獲取qiniu_token和qiniu_key,分別爲%s和%s' % (self.qiniu_token.encode('utf-8'), self.qiniu_key.encode('utf-8'))
43             localfile = '/home/yudahai/PycharmProjects/blog01/app/my-test.png'
44             ret, info = put_file(self.qiniu_token, self.qiniu_key, localfile)
45             print info.status_code
46             if info.status_code == 200:
47                 print '上傳成功'
48                 self.head_picture = self.qiniu_base_url + self.qiniu_key
49                 print '其url爲:' + self.head_picture.encode('utf-8')
50             else:
51                 print '上傳失敗'
52         return response_data
53 
54     def set_head_picture(self, path='/set-head-picture'):
55         payload = {'head_picture': self.head_picture}
56         self.headers = {'token': self.token, 'content-type': 'application/json'}
57         response = requests.post(url=self.base_url + path, data=json.dumps(payload), headers=self.headers)
58         response_data = json.loads(response.content)
59         print response_data.get('message')
60         return response_data
61 
62     def register_step_1(self, phone_number, path='/register-step-1'):
63         payload = {'phone_number': phone_number}
64         self.headers = {'content-type': 'application/json'}
65         response = requests.post(url=self.base_url + path, data=json.dumps(payload), headers=self.headers)
66         response_data = json.loads(response.content)
67         print response_data.get('code')
68         return response_data
69 
70     def register_step_2(self, phone_number, validate_number, path='/register-step-2'):
71         payload = {'phone_number': phone_number, 'validate_number': validate_number}
72         self.headers = {'content-type': 'application/json'}
73         response = requests.post(url=self.base_url + path, data=json.dumps(payload), headers=self.headers)
74         response_data = json.loads(response.content)
75         print response_data.get('code')
76         return response_data
77 
78     def register_step_3(self, phone_number, password, password_confirm, path='/register-step-3'):
79         payload = {'phone_number': phone_number, 'password': password, 'password_confirm': password_confirm}
80         self.headers = {'content-type': 'application/json'}
81         response = requests.post(url=self.base_url + path, data=json.dumps(payload), headers=self.headers)
82         response_data = json.loads(response.content)
83         print response_data.get('code')
84         return response_data
85 
86     def register_step_4(self, phone_number, nickname, path='/register-step-4'):
87         payload = {'phone_number': phone_number, 'nickname': nickname}
88         self.headers = {'content-type': 'application/json'}
89         response = requests.post(url=self.base_url + path, data=json.dumps(payload), headers=self.headers)
90         response_data = json.loads(response.content)
91         print response_data.get('code')
92         return response_data
93 
94 if __name__ == '__main__':
95     api = APITest('http://127.0.0.1:5001')
96     api.login('13565208554', '123456')
97     api.get_qiniu_token()
98     api.set_head_picture()
99     api.logout()

在命令行下驗證一下吧。

整個過程是否是很簡單?基於redis的註冊機制就寫到這。

相關文章
相關標籤/搜索