标签:
我们现在开发app,注册用户的时候,不再像web一样,发送到个人邮箱了,毕竟个人邮箱在移动端填写验证都很麻烦,一般都采用短信验证码的方式。今天我们就讲讲这方面的内容。
首先,先找一个平台吧。我们公司找的容联云通讯这个平台,至少目前为止,用的还可以。先在容联上注册一下,然后创建一个应用,如下图所示:
我只勾选了2个功能,他们这边还有很多其他功能,暂时用不到,就不选了。好了,点击"确认",一个应用就弄好了,下面就尝试着写代码发短信吧。
容联为开发者提供了免费测试功能,但一个号码基本不会超过3次,所以用的时候要小心哦!容联的文档可能写的有点复杂了,反正我觉得稍微有点复杂,其实就是post一个request,我们把它提炼一下,得到下面这些代码。
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‘)
看,这就是重新编辑的函数,是不是很简单,逐行分析一下吧。
首先, accountSid和accountToken 其实就相当于账户的用户名和密码,主要在这个页面可以看到。
其次,appid就是应用的appid,直接填进去就可以
至于templateId,其实就是你添加新的模板的id号,我们这边用开发者账号,直接填写‘1‘就可以了。
好了, 下面的代码,只要熟悉http的人,都会非常熟悉,基本都是把账号的id和token加上时间戳,转换成md5值,然后再encode一下,变成http的基本验证。判别有没有成功,按官网返回的参数,直接解析一下,是‘000000‘就代表成功,否则失败。
是不是很简单?就是这么简单,好了,既然发送短信的函数写好了,下面就写注册api接口吧。
一般的移动注册api接口可以分为3步
1、提交电话号码,发送短信验证,
2、验证短信
3、密码提交,
4、基本资料提交
一共4个接口,就register 1 2 3 4 吧,具体代码如下:
1 # coding:utf-8 2 from flask import Flask, request, jsonify, g 3 from model import User, db_session 4 import hashlib 5 import time 6 import redis 7 import uuid 8 from qiniu import Auth, put_file, etag, urlsafe_base64_encode 9 from util import message_validate 10 import random 11 from functools import wraps 12 13 app = Flask(__name__) 14 redis_store = redis.Redis(host=‘localhost‘, port=6380, db=4, password=‘dahai123‘) 15 access_key = ‘hP7WNicFRHPu2Bd24MaLj5VvmElXYJbRCoZfrVs6‘ 16 secret_key = ‘bBZoaSLKD2lqCZUX6Zu-Fbby5UdTgNVoQT7SfVAV‘ 17 q = Auth(access_key=access_key, secret_key=secret_key) 18 bucket_name = ‘dameinv‘ 19 20 21 def login_check(f): 22 @wraps(f) 23 def decorator(*args, **kwargs): 24 token = request.headers.get(‘token‘) 25 if not token: 26 return jsonify({‘code‘: 0, ‘message‘: ‘需要验证‘}) 27 28 phone_number = redis_store.get(‘token:%s‘ % token) 29 if not phone_number or token != redis_store.hget(‘user:%s‘ % phone_number, ‘token‘): 30 return jsonify({‘code‘: 2, ‘message‘: ‘验证信息错误‘}) 31 32 return f(*args, **kwargs) 33 return decorator 34 35 36 @app.before_request 37 def before_request(): 38 token = request.headers.get(‘token‘) 39 phone_number = redis_store.get(‘token:%s‘ % token) 40 if phone_number: 41 g.current_user = User.query.filter_by(phone_number=phone_number).first() 42 g.token = token 43 return 44 45 46 @app.route(‘/login‘, methods=[‘POST‘]) 47 def login(): 48 phone_number = request.get_json().get(‘phone_number‘) 49 password = request.get_json().get(‘password‘) 50 user = User.query.filter_by(phone_number=phone_number).first() 51 if not user: 52 return jsonify({‘code‘: 0, ‘message‘: ‘没有此用户‘}) 53 54 if user.password != password: 55 return jsonify({‘code‘: 0, ‘message‘: ‘密码错误‘}) 56 57 m = hashlib.md5() 58 m.update(phone_number) 59 m.update(password) 60 m.update(str(int(time.time()))) 61 token = m.hexdigest() 62 63 pipeline = redis_store.pipeline() 64 pipeline.hmset(‘user:%s‘ % user.phone_number, {‘token‘: token, ‘nickname‘: user.nickname, ‘app_online‘: 1}) 65 pipeline.set(‘token:%s‘ % token, user.phone_number) 66 pipeline.expire(‘token:%s‘ % token, 3600*24*30) 67 pipeline.execute() 68 69 return jsonify({‘code‘: 1, ‘message‘: ‘成功登录‘, ‘nickname‘: user.nickname, ‘token‘: token}) 70 71 72 @app.route(‘/user‘) 73 @login_check 74 def user(): 75 user = g.current_user 76 77 nickname = redis_store.hget(‘user:%s‘ % user.phone_number, ‘nickname‘) 78 return jsonify({‘code‘: 1, ‘nickname‘: nickname, ‘phone_number‘: user.phone_number}) 79 80 81 @app.route(‘/logout‘) 82 @login_check 83 def logout(): 84 user = g.current_user 85 86 pipeline = redis_store.pipeline() 87 pipeline.delete(‘token:%s‘ % g.token) 88 pipeline.hmset(‘user:%s‘ % user.phone_number, {‘app_online‘: 0}) 89 pipeline.execute() 90 return jsonify({‘code‘: 1, ‘message‘: ‘成功注销‘}) 91 92 93 @app.route(‘/get-qiniu-token‘) 94 def get_qiniu_token(): 95 key = uuid.uuid4() 96 token = q.upload_token(bucket_name, key, 3600) 97 return jsonify({‘code‘: 1, ‘key‘: key, ‘token‘: token}) 98 99 100 @app.route(‘/set-head-picture‘, methods=[‘POST‘]) 101 @login_check 102 def set_head_picture(): 103 head_picture = request.get_json().get(‘head_picture‘) 104 user = g.current_user 105 user.head_picture = head_picture 106 try: 107 db_session.commit() 108 except Exception as e: 109 print e 110 db_session.rollback() 111 return jsonify({‘code‘: 0, ‘message‘: ‘未能成功上传‘}) 112 redis_store.hset(‘user:%s‘ % user.phone_number, ‘head_picture‘, head_picture) 113 return jsonify({‘code‘: 1, ‘message‘: ‘成功上传‘}) 114 115 116 @app.route(‘/register-step-1‘, methods=[‘POST‘]) 117 def register_step_1(): 118 """ 119 接受phone_number,发送短信 120 """ 121 phone_number = request.get_json().get(‘phone_number‘) 122 user = User.query.filter_by(phone_number=phone_number).first() 123 124 if user: 125 return jsonify({‘code‘: 0, ‘message‘: ‘该用户已经存在,注册失败‘}) 126 validate_number = str(random.randint(100000, 1000000)) 127 result, err_message = message_validate(phone_number, validate_number) 128 129 if not result: 130 return jsonify({‘code‘: 0, ‘message‘: err_message}) 131 132 pipeline = redis_store.pipeline() 133 pipeline.set(‘validate:%s‘ % phone_number, validate_number) 134 pipeline.expire(‘validate:%s‘ % phone_number, 60) 135 pipeline.execute() 136 137 return jsonify({‘code‘: 1, ‘message‘: ‘发送成功‘}) 138 139 140 @app.route(‘/register-step-2‘, methods=[‘POST‘]) 141 def register_step_2(): 142 """ 143 验证短信接口 144 """ 145 phone_number = request.get_json().get(‘phone_number‘) 146 validate_number = request.get_json().get(‘validate_number‘) 147 validate_number_in_redis = redis_store.get(‘validate:%s‘ % phone_number) 148 149 if validate_number != validate_number_in_redis: 150 return jsonify({‘code‘: 0, ‘message‘: ‘验证没有通过‘}) 151 152 pipe_line = redis_store.pipeline() 153 pipe_line.set(‘is_validate:%s‘ % phone_number, ‘1‘) 154 pipe_line.expire(‘is_validate:%s‘ % phone_number, 120) 155 pipe_line.execute() 156 157 return jsonify({‘code‘: 1, ‘message‘: ‘短信验证通过‘}) 158 159 160 @app.route(‘/register-step-3‘, methods=[‘POST‘]) 161 def register_step_3(): 162 """ 163 密码提交 164 """ 165 phone_number = request.get_json().get(‘phone_number‘) 166 password = request.get_json().get(‘password‘) 167 password_confirm = request.get_json().get(‘password_confirm‘) 168 169 if len(password) < 7 or len(password) > 30: 170 # 这边可以自己拓展条件 171 return jsonify({‘code‘: 0, ‘message‘: ‘密码长度不符合要求‘}) 172 173 if password != password_confirm: 174 return jsonify({‘code‘: 0, ‘message‘: ‘密码和密码确认不一致‘}) 175 176 is_validate = redis_store.get(‘is_validate:%s‘ % phone_number) 177 178 if is_validate != ‘1‘: 179 return jsonify({‘code‘: 0, ‘message‘: ‘验证码没有通过‘}) 180 181 pipeline = redis_store.pipeline() 182 pipeline.hset(‘register:%s‘ % phone_number, ‘password‘, password) 183 pipeline.expire(‘register:%s‘ % phone_number, 120) 184 pipeline.execute() 185 186 return jsonify({‘code‘: 1, ‘message‘: ‘提交密码成功‘}) 187 188 189 @app.route(‘/register-step-4‘, methods=[‘POST‘]) 190 def register_step_4(): 191 """ 192 基本资料提交 193 """ 194 phone_number = request.get_json().get(‘phone_number‘) 195 nickname = request.get_json().get(‘nickname‘) 196 197 is_validate = redis_store.get(‘is_validate:%s‘ % phone_number) 198 199 if is_validate != ‘1‘: 200 return jsonify({‘code‘: 0, ‘message‘: ‘验证码没有通过‘}) 201 202 password = redis_store.hget(‘register:%s‘ % phone_number, ‘password‘) 203 204 new_user = User(phone_number=phone_number, password=password, nickname=nickname) 205 db_session.add(new_user) 206 207 try: 208 db_session.commit() 209 except Exception as e: 210 print e 211 db_session.rollback() 212 return jsonify({‘code‘: 0, ‘message‘: ‘注册失败‘}) 213 finally: 214 redis_store.delete(‘is_validate:%s‘ % phone_number) 215 redis_store.delete(‘register:%s‘ % phone_number) 216 217 return jsonify({‘code‘: 1, ‘message‘: ‘注册成功‘}) 218 219 220 @app.teardown_request 221 def handle_teardown_request(exception): 222 db_session.remove() 223 224 if __name__ == ‘__main__‘: 225 app.run(debug=True, host=‘0.0.0.0‘, port=5001)
看到上面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的验证机制就写到这。
标签:
原文地址:http://www.cnblogs.com/yueerwanwan0204/p/5460668.html