Flask project 1 actual combat: 2.3 Use SMS verification code under the flask framework

Insert picture description here

(According to the content of teacher's live broadcast)
  • There are many platforms for sending text messages. This time I take the Ronglian Cloud platform to send text messages as an example.

1. Development of SMS Sending Module

1. Registered users of Ronglian Cloud (free registration, complete personal authentication)

2. Create an application and copy the APP ID and APP TOKEN

Insert picture description here
Insert picture description here
Insert picture description here

3. SMS template

  • If the actual application needs to create a short message template, only the development test does not need to create a new short message template, just use the test template provided by the system
  • Insert picture description here
    Insert picture description here
  • The project uses this template as an example for development and testing
    Insert picture description here

4. Add test number:

  • It’s useless to create a text message template, you can only send text messages to the added test number
  • For development and testing use, add up to 3 mobile phone numbers
    Insert picture description here

5. Refer to the development document to develop the demo code

5.1 View development documents

Insert picture description here
Insert picture description here
Insert picture description here
Insert picture description here

5.2 Copy call example, write ronglianyun_SMS_demo.py

  • appId: ID given by the system when creating the application (it will also appear when editing the application)
  • accId, accToken: just enter the application management to copy, pay attention to confidentiality
  • tid: template ID created by Ronglian Cloud Communication, test template id="1"
  • datas: parameters defined in the template
    Insert picture description here
from ronglian_sms_sdk import SmsSDK
 
accId = '容联云通讯分配的主账号ID'
accToken = '容联云通讯分配的主账号TOKEN'
appId = '容联云通讯分配的应用ID'
 
def send_message():
    sdk = SmsSDK(accId, accToken, appId)
    tid = '1'    # 应用是系统提供的开发测试模板及编号
    mobile = '手机号1,手机号2'
    datas = ('变量1', '变量2')  # 测试模板变量1:短信验证码,变量2:有效时间(分钟)
    resp = sdk.sendMessage(tid, mobile, datas)
    print(resp)  # resp 是string类型,还是json类型
    
send_message()

Insert picture description here

Insert picture description here

6. Develop SMS sending module

  • Create the ronglianyun package under the libs package and build ccp_SMS.py
from ronglian_sms_sdk import SmsSDK
 
accId = '容联云通讯分配的主账号ID'
accToken = '容联云通讯分配的主账号TOKEN'
appId = '容联云通讯分配的应用ID'
 
def send_message():
    sdk = SmsSDK(accId, accToken, appId)
    tid = '1'    # 应用是系统提供的开发测试模板及编号
    mobile = '手机号1,手机号2'
    datas = ('变量1', '变量2') 
    resp = sdk.sendMessage(tid, mobile, datas)
	result=json.load(resp)
    if result["statusCode"]=="000000":
        return 0
    else:
        return -1
        
if __name__=="__main__":
    send_message()
   

7. Optimize the package SMS sending module

  • Every time a short message is sent, the SMS sending module will be invoked, and the SMSSDK class will be instantiated, which consumes a lot of resources, so the singleton mode should be used

7.1 Define the singleton class for sending SMS

  • Define the CCP class and override the __new__() method
    • First determine whether there is an attribute (_instance, custom)
    • Does not exist, call the __new__() method of the parent class super(), assign values ​​to the attributes, and pass in the relevant parameters, and then set the attributes to add an SDK, which is assigned to an instance of the SMS class
    • Finally return the attribute
class CCP(object):
    """发送短信的单例类"""
    def __new__(cls,*args,**kwargs):
        if not hasattr(cls,"_instance"):
            cls._instance=super().__new__(cls,*args,**kwargs)
            cls._instance.sdk=SmsSDK(accId, accToken, appId)
        return cls._instance

7.2 Optimize the SMS sending module

  • Use singleton SMS to send class optimization
  • Send verification code parameterization
from ronglian_sms_sdk import SmsSDK
import json

accId = '容联云通讯分配的主账号ID'
accToken = '容联云通讯分配的主账号TOKEN'
appId = '容联云通讯分配的应用ID'

class CCP(object):
    """发送短信的单例类"""

    def __new__(cls, *args, **kwargs):
        # 如果是第一次实例化,应该返回实例化后的对象,如果是第二次实例化,应该返回上一次实例化后的对象
        # 判断是否存在类属性 _instance
        if not hasattr(cls, "_instance"):
            cls._instance = super().__new__(cls, *args, **kwargs)
            cls._instance.sdk = SmsSDK(accId, accToken, appId)
        return cls._instance

    def send_message(self, tid, mobile, datas):
        sdk = self._instance.sdk
        # tid = '容联云通讯创建的模板ID'
        # mobile = '手机号1,手机号2'
        # datas = ('变量1', '变量2')
        # tid = '1'
        # mobile = '18908656327'
        # datas = ('2345', '5')
        resp = sdk.sendMessage(tid, mobile, datas)
        result = json.loads(resp)
        if result["statusCode"] == "000000":
            return 0
        else:
            return -1


if __name__ == "__main__":
    c = CCP()
    c.send_message("1", "18908656327", ("1234", "5"))    # 测试1号模板,手机号,(短信验证码,有效时间)

2. SMS interface design

1. Logic analysis of SMS verification code

  • First, after the image verification code is correct, an ajax request for SMS verification code is initiated to the backend
  • After the backend receives the request, it first receives and verifies the parameters
  • Verification parameters:
    • Extract the graphic verification code from redis. After extraction, delete the graphic verification code in redis to ensure that the graphic verification code is only used once to prevent library collision
    • Compare whether the passed graphic verification code is the same as the verification code taken out by redis, if they are different, return an error directly
    • If the same, generate SMS verification code and save it in redis
    • Call the SMS sending module to send SMS
    • Send status back to the front end

Insert picture description here

2. Interface document:

  • Interface name
  • description
  • URL
  • Request method
  • Incoming parameters
  • return value

Interface: Get the SMS verification code
Description: The front-end access, you can get the SMS verification code
URL: /api/v1.0/image_codes/<re(r'1[345678]\d{9}')>
Request method: GET
Incoming parameters:

first name Types of Do you have to Description
image_code String Yes The number of the image verification code
image_code_id String Yes uuid

return value:

first name Types of Do you have to Description
errno String no error code
errmsg String no Error content

3. Back-end interface definition (route definition):

  • The routing parameter rule adopts a custom regular expression: <re("regular expression")>
    • Add r before the regular expression character (this is a small pit, it will not be parsed according to the regular expression if it is not added)
  • Get the parameters and ensure that the two parameters must exist: all([list]): Return True if each element of the list is not empty, otherwise return False
  • Take out the image verification code from redis, if it is abnormal, return an error
  • Determine whether the image verification code has expired: the value taken out is None, not obtained, not saved or expired
  • Delete image verification code from redis: to prevent library collision, an image verification code can only be used once, no matter whether it is right or wrong
  • Judge whether the image verification is correct: it is binary data taken out of redis, which needs to be encoded, and then compared with the passed parameters. The verification code is not case sensitive, so you need to change the string to uppercase or lowercase.
  • Determine whether the mobile phone number has been registered: prevent the mobile phone with the registered mobile phone number from re-registering, and check whether there is a mobile phone number in the database
  • Generate SMS verification code: "%06d"% random.randint(0, 999999): Randomly generate a number from 0-999999, which is not enough for 6 digits, add 0 in front
  • Save the SMS verification code to redis and set the expiration time (set as a constant)
  • send messages
@api.route("/sms_codes/<re(r'1[345678]\d{9}'):mobile_code>")
def get_sms_code(mobile_code):
    """获取短信验证码"""
    print("mobile_code=",mobile_code)
    # 获取参数
    # 图片验证码
    image_code = request.args.get('image_code')
    # UUID
    image_code_id = request.args.get('image_code_id')

    print("image_code=",image_code)
    print("image_code_id=",image_code_id)


    # 校验参数,两个参数都不能为空
    if not all([image_code, image_code_id]):
        return jsonify(errno=RET.PARAMERR, errmsg='参数不完整')

    # 业务逻辑
    # 从redis中取出验证码
    try:
        real_redis_code=redis_store.get('image_code_%s' % image_code_id)
    except Exception as e:
        logging.error(e)
        return jsonify(errno=RET.DBERR, errmsg='redis数据库异常')

    # 判断图片验证码是否过期
    if real_redis_code is None:
        return jsonify(errno=RET.NODATA, errmsg='图片验证码失效')

    # 删除redis中的图片验证码
    try:
        redis_store.delete('image_code_%s' % image_code_id)
    except Exception as e:
        logging.error(e)

    # print(real_image_code)  b'RVMJ'
    # 与用户填写的图片验证码对比
    real_redis_code=real_redis_code.decode()
    if image_code.lower()!=real_redis_code.lower():
        return jsonify(errno=RET.PARAMERR, errmsg='图片验证码错误')

    logging.info("real_redis_code="+real_redis_code)

    # 判断手机号是否存在
    try:
        user = User.query.filter_by(mobile=mobile_code).first()
    except Exception as e:
        logging.error(e)
    else:
        if user is not None:
            # 表示手机号已经被注册过
            return jsonify(errno=RET.DATAEXIST, errmsg='手机号已经存在')

    # 生成短信验证码
    sms_code = "%06d" % random.randint(0, 999999)

    # 保存真实的短信验证码到redis
    try:
        redis_store.setex("sms_code_%s" % mobile_code, constants.SMS_CODE_REDIS_EXPIRES, sms_code)
    except Exception as e:
        logging.error(e)
        return jsonify(errno=RET.DBERR, errmsg='保存短信验证码异常')

 # 发短信
    try:
        ccp = CCP()
        result = ccp.send_message(1,mobile_code, (sms_code, int(constants.SMS_CODE_REDIS_EXPIRES/60)))
    except Exception as e:
        logging.error(e)
        return jsonify(errno=RET.THIRDERR, errmsg='发送异常')

    # 返回值
    if result == 0:
        return jsonify(errno=RET.OK, errmsg='发送成功')
    else:
        return jsonify(errno=RET.THIRDERR, errmsg='发送失败')

4. Optimization: avoid frequent sending of SMS verification code logic implementation

  • To prevent malicious and frequent submission of SMS verification requests, it should be limited, and one application per minute

4.1 Logic realization analysis

  • After the SMS verification code is sent successfully, save the SMS verification code successfully sent flag in redis, and set the valid time
  • After receiving the SMS verification code application sent by the user, first determine whether the graphic verification is correct, and then determine whether to request again within the time limit (check the sending status in the redis query, if it exists, it means that the request is sent again within the time limit)
  • Then go to verify the existence of the mobile phone number, which can prevent malicious testing of registered user information
    Insert picture description here

4.2 Code optimization and perfect

  • Extract and verify send_flag
	# 判断手机号的操作
    try:
        send_flag=redis_store.get("send_flag_%s"% mobile_code)
    except Exception as e:
        logging(e)
    else:
        if send_flag is not None:
            return jsonify(errno=RET.RET.REQERR, errmsg='请求过于频繁')

 
  • Rewrite send_flag
 # 保存真实的短信验证码到redis
    try:
        redis_store.setex("sms_code_%s" % mobile_code, constants.SMS_CODE_REDIS_EXPIRES, sms_code)
        # 保存发送给这个手机号的记录
        redis_store.setex("send_flag_%s"% mobile_code, constants.SEND_SMS_CODE_EXPIRES, 1)
    except Exception as e:
        logging.error(e)
        return jsonify(errno=RET.DBERR, errmsg='保存短信验证码异常')

5. Optimization: redis pipeline

  • In the above code, save the SMS verification code and sending status to redis, and perform redis operations twice at the same time, which can be optimized by pipeline
  • For the redis pipeline, see "redis pipeline"
 # 保存真实的短信验证码到redis
    try:
        # redis管道
        pl = redis_store.pipeline()
        pl.setex("sms_code_%s" % mobile_code, constants.SMS_CODE_REDIS_EXPIRES, sms_code)
        # 保存发送给这个手机号的记录
        pl.setex('send_sms_code_%s' % mobile_code, constants.SNED_SMS_CODE_EXPIRES, 1)
        pl.execute()
    except Exception as e:
        logging.error(e)
        return jsonify(errno=RET.DBERR, errmsg='保存短信验证码异常')

6. Cite in the blueprint

  • Put this function and image verification code into one file
from flask import Blueprint

api = Blueprint("api_1_0", __name__, url_prefix="/api/v1.0")

from . import demo,verify_code

3. Application of front-end graphic verification code (omitted)

Guess you like

Origin blog.csdn.net/laoluobo76/article/details/110377427