SMS verification code of Mido mall

1. Logic analysis of SMS verification code

  1. Key points of knowledge:
    • Saving SMS verification code is to prepare for registration.
    • In order to avoid malicious testing by users using graphic verification code, the graphic verification code is deleted immediately after the back-end extracts the graphic verification code.
    • Django does not have the function of sending SMS, so we use the third-party Ronglian cloud communication SMS platform to help us send SMS verification code.

2. Ronglian cloud communication SMS platform

Objective: to understand the usage of Rong intermodal communication platform and sms SDK
Steps:

  • Understanding of ronglianyun communication SMS platform
  • Ronglianyun communication sms SDK test
  • Encapsulate sending SMS singleton class
    • Problem: if multiple SMS verification codes are sent at the same time, multiple RET SDK objects will be created at the same time, which will consume additional memory space
    • Solution:
      • Singleton design pattern
        • Features: a singleton class has only one instance
        • Usage scenario: when there is only one instance of a class in the whole system, the singleton design pattern can be used
  1. Introduction to Ronglian cloud communication SMS platform

    1. Rong Lianyun official website

    • Register and log in
    1. Ronglian cloud management console
      Developer master account:

      1. ACCOUNT SID: unique identification of the account
      2. AUTH TOKEN: authentication password, equivalent to password
      3. Rest URL (production): the request address when docking with the platform (online use, non Development)
      4. Appid (default): assigned when applying for sub applications,
    2. Ronglian cloud creation application

    3. Apply for online application and conduct qualification certification

    4. The qualification certification is completed and the application is successfully launched

    5. Add test number

    6. SMS template

  2. Ronglian cloud communication sms SDK test

    1. Template sms SDK Download: link

    2. Instructions for using template sms SDK: link

  3. Integrated template sms SDK

    • CCPRestSDK.py: the official SDK file written by the developer of Ronglian cloud communication, including the method of sending template SMS
    • ccp_sms.py: call the method of sending template SMS
  4. Template sms SDK test

    • ccp_sms.py file
      # -*- coding:utf-8 -*-
      
      from verifications.libs.yuntongxun.CCPRestSDK import REST
      
      # Note: after logging in to the cloud communication website, you can see the developer's main account ACCOUNT SID in "console - Application"
      _accountSid = 'ACCOUNT SID'
      
      # Note: after logging in to the cloud communication website, you can see the developer's main account AUTH TOKEN in the console - application
      _accountToken = 'AUTH TOKEN'
      
      # Please use the APPID on the homepage of the management console or create your own APPID
      _appId = 'APPID'
      
      # Note: the request address is configured as app.cloopen.com in the production environment
      _serverIP = 'app.cloopen.com'
      
      # Description: request port, production environment is 8883
      _serverPort = "8883"
      
      # Note: the REST API version number remains unchanged
      _softVersion = '2013-12-26'
      
      # Example of sending SMS code officially provided by cloud communication
      # Send template SMS
      # @param to mobile number
      # @The format of param data content data is array, for example: {'12', '34'}. If replacement is not required, please fill in ''
      # @param $tempId template Id
      def sendTemplateSMS(to, datas, tempId):
          # Initialize REST SDK
          rest = REST(_serverIP, _serverPort, _softVersion)
          rest.setAccount(_accountSid, _accountToken)
          rest.setAppId(_appId)
      
          result = rest.sendTemplateSMS(to, datas, tempId)
          print(result)
          for k, v in result.items():
      
              if k == 'templateSMS':
                  for k, s in v.items():
                      print('%s:%s' % (k, s))
              else:
                  print('%s:%s' % (k, v))
      
      if __name__ == '__main__':
          # Note: the SMS template number of the test is 1
          sendTemplateSMS('17600992168', ['123456', 5], 1)
      
  5. Description of returned results of template sms SDK

    {
        'statusCode': '000000', // Status code. " 000000 'means success, otherwise, it means failure
        'templateSMS': 
            {
                'smsMessageSid': 'b5768b09e5bc4a369ed35c444c13a1eb', // SMS unique identifier
                'dateCreated': '20190125185207' // SMS sending time
            }
    }
    
  6. Encapsulate sending SMS singleton class

    1. Encapsulate sending SMS singleton class

      class CCP(object):
          """Singleton class for sending SMS"""
      
          def __new__(cls, *args, **kwargs):
              # Determine whether class attributes exist_ instance,_ Instance is the only object of class CCP, i.e. singleton
              if not hasattr(CCP, "_instance"):
                  cls._instance = super(CCP, cls).__new__(cls, *args, **kwargs)
                  cls._instance.rest = REST(_serverIP, _serverPort, _softVersion)
                  cls._instance.rest.setAccount(_accountSid, _accountToken)
                  cls._instance.rest.setAppId(_appId)
              return cls._instance
      
    2. Encapsulation sending SMS singleton method

      def send_template_sms(self, to, datas, temp_id):
          """
          Sending template SMS single example method
          :param to: Registered mobile number
          :param datas: Template SMS content data, in the format of list, for example:['123456', 5],If no replacement is required, please fill in ''
          :param temp_id: The template number is provided free of charge by default id Template with 1
          :return: Texting results
          """
          result = self.rest.sendTemplateSMS(to, datas, temp_id)
          if result.get("statusCode") == "000000":
              # Return 0, indicating that the SMS is sent successfully
              return 0
          else:
              # Return - 1, indicating that the sending failed
              return -1
      
    3. Sending template SMS results by test singleton class

      if __name__ == '__main__':
          # Note: the SMS template number of the test is 1
          CCP().send_template_sms('17600992168', ['123456', 5], 1)
      
  7. Key points of knowledge

    • Ronglian cloud communication is only one of the platforms for sending SMS. There are other cloud platforms available, such as Alibaba cloud. The implementation routines are interlinked.
    • Encapsulating the class of sending SMS as a single example belongs to a scheme of performance optimization.

3. SMS verification code backend logic

Objective: to complete the back-end logic of sending SMS verification code with the help of Ronglian cloud communication
Steps:

  • Design and define back-end interfaces
  • Implement backend logic
  1. Design of SMS verification code interface
    1. Request mode
optionprogramme
Request methodGET
Request address/sms_codes/(?P1[3-9]\d{9})/
		2. Request parameters: path parameters and query strings
Parameter nametypeIs it necessary to passexplain
mobilestringyescell-phone number
image_codestringyesGraphic verification code
uuidstringyesUnique number
		3. Response result: JSON
fieldexplain
codeStatus code
errmsginformation
  1. SMS verification code interface definition

    class SMSCodeView(View):
        """SMS verification code"""
    
        def get(self, reqeust, mobile):
            """
            :param reqeust: Request object
            :param mobile: cell-phone number
            :return: JSON
            """
            pass
    
  2. Back end logic implementation of SMS verification code

    class SMSCodeView(View):
        """SMS verification code"""
    
        def get(self, reqeust, mobile):
            """
            :param reqeust: Request object
            :param mobile: cell-phone number
            :return: JSON
            """
            # Receive parameters
            image_code_client = reqeust.GET.get('image_code')
            uuid = reqeust.GET.get('uuid')
    
            # Calibration parameters
            if not all([image_code_client, uuid]):
                return http.JsonResponse({'code': RETCODE.NECESSARYPARAMERR, 'errmsg': 'Missing required parameters'})
    
            # Create an object connected to redis
            redis_conn = get_redis_connection('verify_code')
            # Extract graphic verification code
            image_code_server = redis_conn.get('img_%s' % uuid)
            if image_code_server is None:
                # The graphic verification code is out of date or does not exist
                return http.JsonResponse({'code': RETCODE.IMAGECODEERR, 'errmsg': 'Invalid graphic verification code'})
            # Delete the graphic verification code to avoid malicious testing of the graphic verification code
            try:
                redis_conn.delete('img_%s' % uuid)
            except Exception as e:
                logger.error(e)
            # Comparison graphic verification code
            image_code_server = image_code_server.decode()  # bytes to string
            if image_code_client.lower() != image_code_server.lower():  # Compare after converting to lowercase
                return http.JsonResponse({'code': RETCODE.IMAGECODEERR, 'errmsg': 'Error in input graphic verification code'})
    
            # Generate SMS verification code: generate a 6-digit verification code
            sms_code = '%06d' % random.randint(0, 999999)
            logger.info(sms_code)
            # Save SMS verification code
            redis_conn.setex('sms_%s' % mobile, constants.SMS_CODE_REDIS_EXPIRES, sms_code)
            # Send SMS verification code
            CCP().send_template_sms(mobile,[sms_code, constants.SMS_CODE_REDIS_EXPIRES // 60], constants.SEND_SMS_TEMPLATE_ID)
    
            # Response results
            return http.JsonResponse({'code': RETCODE.OK, 'errmsg': 'SMS sent successfully'})
    

4. SMS verification code front-end logic

Purpose: the two-way binding of Vue.js will be used to show the effect of countdown of 60 seconds
Steps:

  • axios sends ajax request to get SMS verification code
  • Show the countdown of 60 seconds
  • SMS verification code user interaction and verification
  1. Vue binding SMS verification code interface

    1. register.html

      		<li>
      		    <label>SMS verification code:</label>
      		    <input type="text" v-model="sms_code" @blur="check_sms_code" name="sms_code" id="msg_code" class="msg_input">
      		    <a @click="send_sms_code" class="get_msg_code">[[ sms_code_tip ]]</a>
      		    <span class="error_tip" v-show="error_sms_code">[[ error_sms_code_message ]]</span>
      		</li>
      
    2. register.js

      check_sms_code(){
          if(this.sms_code.length != 6){
              this.error_sms_code_message = 'Please fill in the SMS verification code';
              this.error_sms_code = true;
          } else {
              this.error_sms_code = false;
          }
      },
      
  2. axios request SMS verification code

    1. Event handling of sending SMS verification code

      send_sms_code(){
          // Avoid repeated clicks
          if (this.sending_flag == true) {
              return;
          }
          this.sending_flag = true;
      
          // Calibration parameters
          this.check_mobile();
          this.check_image_code();
          if (this.error_mobile == true || this.error_image_code == true) {
              this.sending_flag = false;
              return;
          }
      
          // Request SMS verification code
          let url = '/sms_codes/' + this.mobile + '/?image_code=' + this.image_code+'&uuid='+ this.uuid;
          axios.get(url, {
              responseType: 'json'
          })
              .then(response => {
                  if (response.data.code == '0') {
                      // Countdown 60 seconds
                      var num = 60;
                      var t = setInterval(() => {
                          if (num == 1) {
                              clearInterval(t);
                              this.sms_code_tip = 'Get SMS verification code';
                              this.sending_flag = false;
                          } else {
                              num -= 1;
                              // Show countdown information
                              this.sms_code_tip = num + 'second';
                          }
                      }, 1000, 60)
                  } else {
                      if (response.data.code == '4001') {
                          this.error_image_code_message = response.data.errmsg;
                          this.error_image_code = true;
                      } else { // 4002
                          this.error_sms_code_message = response.data.errmsg;
                          this.error_sms_code = true;
                      }
                      this.generate_image_code();
                      this.sending_flag = false;
                  }
              })
              .catch(error => {
                  console.log(error.response);
                  this.sending_flag = false;
              })
      },
      
  3. SMS verification code effect display

5. Supplement SMS verification logic during registration

Objective: to improve the SMS verification logic in the registration process
Steps:

  • SMS verification backend logic
    • Compare whether the SMS verification codes entered by the user and stored in Redis are consistent
  • SMS verification function front end logic
  1. Supplementary SMS verification back-end logic during registration

    1. Received SMS verification code parameters

      sms_code_client = request.POST.get('sms_code')
      
    2. Before saving the registration data, compare the SMS verification code

      redis_conn = get_redis_connection('verify_code')
      sms_code_server = redis_conn.get('sms_%s' % mobile)
      if sms_code_server is None:
          return render(request, 'register.html', {'sms_code_errmsg':'Invalid SMS verification code'})
      if sms_code_client != sms_code_server.decode():
          return render(request, 'register.html', {'sms_code_errmsg': 'Error in entering SMS verification code'})
      
  2. Supplement SMS verification front-end logic during registration

    1. register.html

      <li>
          <label>SMS verification code:</label>
          <input type="text" v-model="sms_code" @blur="check_sms_code" name="sms_code" id="msg_code" class="msg_input">
          <a @click="send_sms_code" class="get_msg_code">[[ sms_code_tip ]]</a>
          <span v-show="error_sms_code" class="error_tip">[[ error_sms_code_message ]]</span>
          {% if sms_code_errmsg %}
              <span class="error_tip">{{ sms_code_errmsg }} </span>
          {% endif %}
      </li>
      

6. Avoid sending SMS verification code frequently

Existing problems:

  • Although we did a 60 second countdown function on the front-end interface. However, malicious users can bypass the front-end interface and frequently request SMS verification codes from the back-end.

terms of settlement:

  • At the back end, the frequency of users requesting SMS verification code should also be limited. Only one request for SMS verification code is allowed within 60 seconds.
  • Cache a value in Redis database, and the validity period is set to 60 seconds.
  1. Logic analysis of avoiding frequently sending SMS verification code

  2. Logic implementation of avoiding frequently sending SMS verification code

    1. Extract and verify send_flag

      send_flag = redis_conn.get('send_flag_%s' % mobile)
      if send_flag:
          return http.JsonResponse({'code': RETCODE.THROTTLINGERR, 'errmsg': 'Sending text messages too often'})
      
    2. Re write send_flag

      # Save SMS verification code
      redis_conn.setex('sms_%s' % mobile, constants.SMS_CODE_REDIS_EXPIRES, sms_code)
      # Re write send_flag
      redis_conn.setex('send_flag_%s' % mobile, constants.SEND_SMS_CODE_INTERVAL, 1)
      
    3. Interface rendering frequently sends SMS prompt information

      if (response.data.code == '4001') {
          this.error_image_code_message = response.data.errmsg;
          this.error_image_code = true;
      } else { // 4002
          this.error_sms_code_message = response.data.errmsg;
          this.error_sms_code = true;
      }
      

7.pipeline operation Redis database

Redis C - S architecture:

  • TCP service based on client server model and request / response protocol.
  • The client sends a query request to the server and listens for the Socket return.
  • It is usually in blocking mode, waiting for the server to respond.
  • The server processes the command and returns the result to the client.

Existing problems:

  • If the Redis server needs to process multiple requests at the same time, coupled with network latency, the server utilization is not high and the efficiency is reduced.

Solution:

  • pipeline
  1. Introduction to pipeline

    • pipeline

      • You can send multiple commands at one time and return the results at one time after execution.
      • pipeline reduces the round-trip delay time by reducing the number of communication between the client and Redis.
    • Principle of implementation

      • The principle of implementation is queue.
      • The Client can send three commands together in one tcp message.
      • Server can put the processing results of the three commands into a tcp message for return.
      • The queue is first in first out, which ensures the order of data.
  2. pipeline operation Redis database

    1. Implementation steps

      1. Create Redis pipeline
      2. Add Redis request to queue
      3. Execute request
    2. code implementation

      # Create Redis pipeline
      pl = redis_conn.pipeline()
      # Add Redis request to queue
      pl.setex('sms_%s' % mobile, constants.SMS_CODE_REDIS_EXPIRES, sms_code)
      pl.setex('send_flag_%s' % mobile, constants.SEND_SMS_CODE_INTERVAL, 1)
      # Execute request
      pl.execute()
      


  • As mortals, as gods

Tags: Python Django Back-end

Posted on Mon, 01 Nov 2021 20:33:42 -0400 by dandelo