短信验证码的设计与实现

时间:2024-02-16 08:08:50

准备:
  1.容联云通信注册账号并下载设置

  2.将yuntongxun文件夹复制到项目目录下

短信验证码业务分析: 需要使用redis数据库 配置文件里添加如下设置

CACHES = {
    "default": { # 默认的缓存配置
        "BACKEND": "django_redis.cache.RedisCache",
        "LOCATION": "redis://127.0.0.1:6379/0",
        "OPTIONS": {
            "CLIENT_CLASS": "django_redis.client.DefaultClient",
        }
    },
    # 我的需求是希望将session存储在redis的1号库
    "session": { # session后端
        "BACKEND": "django_redis.cache.RedisCache",
        "LOCATION": "redis://127.0.0.1:6379/1",
        "OPTIONS": {
            "CLIENT_CLASS": "django_redis.client.DefaultClient",
        }
    },
    "verify_code": { # 验证码
        "BACKEND": "django_redis.cache.RedisCache",
        "LOCATION": "redis://127.0.0.1:6379/2",
        "OPTIONS": {
            "CLIENT_CLASS": "django_redis.client.DefaultClient",
        }
    },
}

 

短信验证码后端接口设计:

1.请求方式
选项    方案
请求方法    GET
请求地址    /sms_codes/(?P<mobile>1[3-9]\d{9})/
请求参数:路径参数和查询字符串传参
其中:mobile 是用路径传递参数的, image_code 和 image_code_id 是用查询字符串传递的参数.

2.参数名    类型    是否必传    说明
mobile    string    是      手机号
image_code    string   是    图形验证码
image_code_id    string   是    唯一编号

3.响应结果:JSON
字段    说明
code    状态码
errmsg    错误信息

 短信验证码实现 在视图集文件里添加代码

class SMSCodeView(View):

    def get(self, reqeust, mobile):
        # 避免频繁发送短信验证码
        redis_conn = get_redis_connection(\'verify_code\')

        send_flag = redis_conn.get(\'send_flag_%s\' % mobile)
        if send_flag:
            return http.JsonResponse({\'code\': 400,
                                      \'errmsg\': \'发送短信过于频繁\'})
     
        image_code_client = reqeust.GET.get(\'image_code\')
        uuid = reqeust.GET.get(\'image_code_id\')

        if not all([image_code_client, uuid]):
            return http.JsonResponse({\'code\': 400,
                                      \'errmsg\': \'缺少必传参数\'})

        image_code_server = redis_conn.get(\'img_%s\' % uuid)
        if image_code_server is None:
            return http.JsonResponse({\'code\': 400,
                                      \'errmsg\': \'图形验证码失效\'})
        try:
            redis_conn.delete(\'img_%s\' % uuid)
        except Exception as e:
            logger.error(e)

        image_code_server = image_code_server.decode()
        if image_code_client.lower() != image_code_server.lower():
            return http.JsonResponse({\'code\': 400,
                                      \'errmsg\': \'输入图形验证码有误\'})

        sms_code = \'%06d\' % random.randint(0, 999999)
        logger.info(sms_code)

        # 创建管道对象: 
        pl = redis_conn.pipeline()

        # redis_conn.setex(\'sms_%s\' % mobile, 300, sms_code)
        pl.setex(\'sms_%s\' % mobile, 300, sms_code)

        # redis_conn.setex(\'send_flag_%s\' % mobile, 60, 1)
        pl.setex(\'send_flag_%s\' % mobile, 60, 1)

        # 执行管道: 
        pl.execute()

        CCP().send_template_sms(mobile, [sms_code, 5], 1)

        return http.JsonResponse({\'code\': 0,
                                  \'errmsg\': \'发送短信成功\'})

配置短信验证码的url  

urlpatterns = [
    re_path(r"^sms_codes/(?P<mobile>1[3-9]\d{9})/$", views.SMSCodeView.as_view()), 
]

 

短信验证码后端常见优化:

1. 如何避免频繁发送短信验证码:
  在后端也要限制用户请求短信验证码的频率.
  60秒内只允许一次请求短信验证码.
  在 Redis 数据库中缓存一个数值,有效期设置为60秒.

2. Redis服务端问题:
  如果Redis服务端需要同时处理多个请求,加上网络延迟,
  那么服务端利用率不高,效率降低。
  解决方案:pipeline
  1. 创建redis请求管道
  2. 将操作redis的多个命令请求添加到管道队列里
  3. 执行命令

3.发送短信是耗时操作,如果短信被阻塞,用户响应会延迟,影响用户界面倒计时延迟
  """
  CCP().send_template_sms(mobile, [sms_code, 5], 1)
  return http.JsonResponse({\'code\': 0, \'errmsg\': \'发送短信成功\'})
  """
  解决方案:异步方案redis 和 Celey
  异步发送短信
  将发送短信业务和响应分开执行,将发送短信 从主业务中 解耦出来