阿里云验证码短信发送服务搭建(flask)

参考:https://next.api.aliyun.com/api-tools/sdk/Dysmsapi?version=2017-05-25&language=java-async-tea&tab=primer-doc

我们需要思考验证服务一些要求:

1.验证码只能被验证一次,所以需要状态字段

2.验证码有失效时间,超出时间后则失效

3.验证码有限制次数,比如1min只能发送一次,1h只能发送xx次,一天只能发送xx次

4.验证码由random包生成四位随机数字

因此,我们创建数据表

sql 复制代码
CREATE TABLE `verification_code` (
  `id` int NOT NULL AUTO_INCREMENT,
  `phone` varchar(11) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT '' COMMENT '手机号码',
  `code` varchar(6) DEFAULT '' COMMENT '验证码',
  `channel` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT '' COMMENT '渠道名称',
  `template_code` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT '' COMMENT '模板代码',
  `status` tinyint DEFAULT '10' COMMENT '状态',
  `create_time` datetime DEFAULT CURRENT_TIMESTAMP,
  `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  `type` tinyint DEFAULT '1' COMMENT '类型 1:验证码短信 2:通知短信',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

项目结构如下:

web.py是我们的主文件

注意作者此处使用了数据库操作方法更改状态,读者可以使用redis等数据库操作

python 复制代码
from datetime import datetime, timedelta
 
from flask import request, jsonify
import random

from utils import Sample
from my_app.crud.crud_SMS import CRUDSMS

app = Flask(__name__)


# 统一响应数据格式
def response(code=200, message="请求成功!", data=None):
    res = {
        'code': code,
        'message': message,
        'data': data if data is not None else {}
    }
    return jsonify(res)


# 定义一个错误处理器,捕获所有的异常
@app.errorhandler(Exception)
def handle_error(e):
    return response(400, str(e))


@app.route('/sms_message/verify_code', methods=['POST'])
def verify():
    data = request.get_json()
    phone = data.get('phone')
    code = data.get('code')
    channel = data.get('channel')
    result = CRUDSMS.get(phone, channel)
    current_time = datetime.now()
    # 判断 验证码是否有效
    if result:

        if current_time - result['create_time'] > timedelta(minutes=5):
            return response(401, message="验证码已过期")
        # 验证码是否正确
        if code == result['code']:
            if result['status'] == 99:
                return response(401, message="验证码已被验证过了,请勿重复使用")
            else:
                CRUDSMS.update(result["id"],{"status":99}) #过期失效
                return response(200, message="验证成功")
        else:
            return response(405, message="验证码错误")
    else:
        return response(406, message="该手机未发送过验证码")


@app.route('/sms_message/getVerificationCode', methods=['POST'])
def getVerificationCode():
    data = request.get_json()
    channel = data.get('channel')
    phone = data.get('phone')
    # 随机生成四位数字
    random_number_str = ''.join([str(random.randint(0, 9)) for _ in range(4)])
    template_code = "xxx"

    result = CRUDSMS.get(phone, channel)
    current_time = datetime.now()
    # 判断 验证码是在1min之内发送过
    if result and current_time - result['create_time'] < timedelta(minutes=1):
        return response(401, message="短信1分钟之内已经发送过,请稍后再试")
    # 判断用户最近一小时发过几次短信
    # 计算过去 1 小时的时间点
    one_hour_ago = current_time - timedelta(hours=1)
    one_day_ago = current_time - timedelta(days=1)
    past_hour_message_num = CRUDSMS.count(phone, channel, one_hour_ago)
    past_day_message_num = CRUDSMS.count(phone, channel, one_day_ago)
    if past_hour_message_num > 5:
        return response(402, message="短信1小时之内已经发送过5条,超出上限,请稍后再试")
    if past_day_message_num > 10:
        return response(403, message="短信1天之内已经发送过10条,超出上限,请稍后再试")
    result = Sample.getVerificationCode(template_code, phone, random_number_str)
    if result:
        CRUDSMS.create(
            {"type": 1, "channel": channel, "phone": phone, "code": random_number_str, "template_code": template_code,
             "create_time": datetime.now().strftime('%Y-%m-%d %H:%M:%S')})
        return response(200, data=random_number_str)

    return response(400, message="短信发送失败")


@app.route('/sms_message/sendNotice', methods=['POST'])
def sendNotice():
    # 从请求中获取union_id
    data = request.get_json()
    template_code = data.get('template_code')
    channel = data.get('channel')
    template_param = data.get('template_param','')
    phone = data.get('phone')
    result = Sample.sendNotice(template_code, phone, template_param)
    if result:
        # 存入数据库
        CRUDSMS.create({"type": 2, "channel": channel, "phone": phone, "code": "", "template_code": template_code,
                        "create_time": datetime.now().strftime('%Y-%m-%d %H:%M:%S')})
        return response(200)

    return response(400, message="短信发送失败")


if __name__ == '__main__':
    app.run(debug=True, host="0.0.0.0", port=5000)

utils.py文件是操作阿里云接口的方法:

填写sign_name、 ALIBABA_CLOUD_ACCESS_KEY_ID、ALIBABA_CLOUD_ACCESS_KEY_SECRET

python 复制代码
# -*- coding: utf-8 -*-
# This file is auto-generated, don't edit it. Thanks.
import os
import sys

from typing import List

from alibabacloud_dysmsapi20170525.client import Client as Dysmsapi20170525Client
from alibabacloud_tea_openapi import models as open_api_models
from alibabacloud_dysmsapi20170525 import models as dysmsapi_20170525_models
from alibabacloud_tea_util import models as util_models
from alibabacloud_tea_util.client import Client as UtilClient
from logger import normal_log,error_log
ALIBABA_CLOUD_ACCESS_KEY_ID = ""
ALIBABA_CLOUD_ACCESS_KEY_SECRET = ""
class Sample:
    def __init__(self):
        pass

    @staticmethod
    def create_client() -> Dysmsapi20170525Client:
        """
        使用AK&SK初始化账号Client
        @return: Client
        @throws Exception
        """
        # 工程代码泄露可能会导致 AccessKey 泄露,并威胁账号下所有资源的安全性。以下代码示例仅供参考。
        # 建议使用更安全的 STS 方式,更多鉴权访问方式请参见:https://help.aliyun.com/document_detail/378659.html。
        config = open_api_models.Config(
            # 必填,请确保代码运行环境设置了环境变量 ALIBABA_CLOUD_ACCESS_KEY_ID。,
            access_key_id=ALIBABA_CLOUD_ACCESS_KEY_ID,
            # 必填,请确保代码运行环境设置了环境变量 ALIBABA_CLOUD_ACCESS_KEY_SECRET。,
            access_key_secret=ALIBABA_CLOUD_ACCESS_KEY_SECRET
        )
        # Endpoint 请参考 https://api.aliyun.com/product/Dysmsapi
        config.endpoint = f'dysmsapi.aliyuncs.com'
        return Dysmsapi20170525Client(config)

    @staticmethod
    def getVerificationCode(template_code,phone_numbers,code):
        client = Sample.create_client()
        send_sms_request = dysmsapi_20170525_models.SendSmsRequest(
            phone_numbers=phone_numbers,
            sign_name='xxx',
            template_code = template_code,
            template_param  = '{"code":"%s"}'%code,
        )
        try:
            # 复制代码运行请自行打印 API 的返回值
            client.send_sms_with_options(send_sms_request, util_models.RuntimeOptions())
            normal_log.logger.info("{} 发送验证码成功".format(phone_numbers))
            return True

        except Exception as error:
            # 此处仅做打印展示,请谨慎对待异常处理,在工程项目中切勿直接忽略异常。
            # 错误 message
            print("{} 发送验证码失败{}".format(phone_numbers, str(error)))
            return False


    @staticmethod
    def sendNotice(
            template_code, phone_numbers,template_param
    ):
        client = Sample.create_client()
        send_sms_request = dysmsapi_20170525_models.SendSmsRequest(
            phone_numbers=phone_numbers,
            sign_name='',
            template_code=template_code,
            template_param=template_param
        )
        try:
            # 复制代码运行请自行打印 API 的返回值
            client.send_sms_with_options(send_sms_request, util_models.RuntimeOptions()) 
            return True


        except Exception as error:
            ...
相关推荐
IvorySQL29 分钟前
PostgreSQL 分区表的 ALTER TABLE 语句执行机制解析
数据库·postgresql·开源
·云扬·39 分钟前
MySQL 8.0 Redo Log 归档与禁用实战指南
android·数据库·mysql
IT邦德42 分钟前
Oracle 26ai DataGuard 搭建(RAC到单机)
数据库·oracle
惊讶的猫1 小时前
redis分片集群
数据库·redis·缓存·分片集群·海量数据存储·高并发写
不爱缺氧i1 小时前
完全卸载MariaDB
数据库·mariadb
纤纡.1 小时前
Linux中SQL 从基础到进阶:五大分类详解与表结构操作(ALTER/DROP)全攻略
linux·数据库·sql
jiunian_cn2 小时前
【Redis】渐进式遍历
数据库·redis·缓存
橙露2 小时前
Spring Boot 核心原理:自动配置机制与自定义 Starter 开发
java·数据库·spring boot
冰暮流星2 小时前
sql语言之分组语句group by
java·数据库·sql
符哥20082 小时前
Ubuntu 常用指令集大全(附实操实例)
数据库·ubuntu·postgresql