- テキストメッセージを送信するためのプラットフォームはたくさんありますが、今回はRonglianCloudプラットフォームを例としてテキストメッセージを送信します。
1.SMS送信モジュールの開発
1. Ronglian Cloudの登録ユーザー(無料登録、完全な個人認証)
2.アプリケーションを作成し、APPIDとAPPトークンをコピーします
3.SMSテンプレート
- 実際のアプリケーションでショートメッセージテンプレートを作成する必要がある場合は、開発テストのみで新しいショートメッセージテンプレートを作成する必要はありません。システムが提供するテストテンプレートを使用するだけです。
- プロジェクトでは、このテンプレートを開発とテストの例として使用します
4.テスト番号を追加します。
- SMSテンプレートを作成するのは無意味です。SMSはテスト番号にのみ送信できます。
- 開発およびテスト用に、最大3つの携帯電話番号を追加します
5.デモコードを開発するには、開発ドキュメントを参照してください
5.1開発ドキュメントを表示する
5.2呼び出し例をコピーし、ronglianyun_SMS_demo.pyと記述します
- appId:アプリケーションの作成時にシステムによって指定されたID(アプリケーションの編集時にも表示されます)
- accId、accToken:アプリケーション管理を入力してコピーし、機密性に注意してください
- tid:Ronglian Cloud Communicationによって作成されたテンプレートID、テストテンプレートid = "1"
- データ:テンプレートで定義されたパラメーター
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()
6.SMS送信モジュールを開発する
- libsパッケージの下にronglianyunパッケージを作成し、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.パッケージSMS送信モジュールを最適化します
- ショートメッセージが送信されるたびに、SMS送信モジュールが呼び出され、SMSSDKクラスがインスタンス化されます。これは多くのリソースを消費するため、シングルトンモードを使用する必要があります。
7.1SMSを送信するためのシングルトンクラスを定義する
- CCPクラスを定義し、__ new __()メソッドをオーバーライドします
- まず、属性(_instance、custom)があるかどうかを判断します
- 存在しない場合は、親クラスsuper()の__new __()メソッドを呼び出し、属性に値を割り当て、関連するパラメーターを渡してから、属性を設定して、のインスタンスに割り当てられるSDKを追加します。 SMSクラス
- 最後に属性を返します
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送信SMSモジュールを最適化する
- シングルトンSMSを使用してクラスの最適化を送信する
- 確認コードのパラメーター化を送信する
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インターフェースの設計
1.SMS検証コードの論理分析
- まず、画像検証コードが正しい後、SMS検証コードのajaxリクエストがバックエンドに対して開始されます
- バックエンドはリクエストを受信した後、最初にパラメータを受信して検証します
- 検証パラメータ:
- redisからグラフィック検証コードを抽出します。抽出後、redisのグラフィック検証コードを削除して、ライブラリの衝突を防ぐためにグラフィック検証コードが1回だけ使用されるようにします。
- 渡されたグラフィック検証コードがredisによって取り出された検証コードと同じであるかどうかを比較します。それらが異なる場合は、エラーを直接返します。
- 同じ場合は、SMS確認コードを生成してredisに保存します
- SMS送信モジュールを呼び出してSMSを送信します
- ステータスをフロントエンドに送り返す
2.インターフェイスドキュメント:
- インターフェイス名
- 説明
- URL
- リクエスト方法
- 着信パラメータ
- 戻り値
インターフェース:SMS検証コードを取得
説明:フロントエンドにアクセスすると、SMS検証コード番号を取得でき
ますURL:/api/v1.0/image_codes/ <re(r'1 [345678] \ d {9} ')>
リクエストメソッド:GET
受信パラメータ:
ファーストネーム の種類 あなたはする必要がありますか 説明 image_code ストリング はい 画像確認コードの番号 image_code_id ストリング はい uuid 戻り値:
ファーストネーム の種類 あなたはする必要がありますか 説明 errno ストリング 番号 エラーコード errmsg ストリング 番号 エラー内容
3.バックエンドインターフェイス定義(ルート定義):
- ルーティングパラメータルールは、カスタム正規表現を採用しています:<re( "regular expression")>
- パラメータを取得し、2つのパラメータが存在する必要があることを確認します。all([list]):リストの各要素が空でない場合はTrueを返し、そうでない場合はFalseを返します。
- redisから画像検証コードを取り出し、異常な場合はエラーを返します
- 画像検証コードの有効期限が切れているかどうかを確認します。取り出された値は「なし」、「取得されていない」、「保存されていない」、「期限切れ」です。
- redisから画像検証コードを削除する:ライブラリの衝突を防ぐために、画像検証コードは、それが正しいか間違っているかに関係なく、一度だけ使用できます
- 画像検証が正しいかどうかを判断します。これは、redisから取り出されたバイナリデータであり、エンコードして、渡されたパラメータと比較する必要があります。検証コードでは大文字と小文字が区別されないため、文字列を大文字または小文字に変更する必要があります。 。
- 携帯電話番号が登録されているかどうかを確認します。登録されている携帯電話番号の携帯電話が再登録されないようにし、データベースに携帯電話番号があるかどうかを確認します。
- SMS検証コードを生成します: "%06d"%random.randint(0、999999):0から999999までの数値をランダムに生成します。これは6桁では不十分であり、前に0を追加します
- SMS確認コードを保存してredisし、有効期限を設定します(定数として設定)
- メッセージを送る
@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.最適化:SMS検証コードロジック実装の頻繁な送信を回避します
- SMS検証要求の悪意のある頻繁な送信を防ぐために、1分あたり1つのアプリケーションに制限する必要があります
4.1論理実現分析
- SMS検証コードが正常に送信されたら、SMS検証コードが正常に送信されたフラグをredisに保存し、有効時間を設定します
- ユーザーから送信されたSMS検証コードアプリケーションを受信した後、最初にグラフィック検証が正しいかどうかを判断し、次に制限時間内に再度リクエストするかどうかを判断します(redisクエリで送信ステータスを確認します(存在する場合)。制限時間内にリクエストが再度送信されます)
- 次に、携帯電話番号の存在を確認します。これにより、登録されたユーザー情報の悪意のあるテストを防ぐことができます。
4.2コードの最適化と完璧
- 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='请求过于频繁')
- 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.最適化:redisパイプライン
- 上記のコードでは、SMS検証コードと送信ステータスをredisに保存し、redis操作を同時に2回実行します。これは、パイプラインによって最適化できます。
- redisパイプラインについては、「redisパイプライン」を参照してください。
# 保存真实的短信验证码到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.青写真で引用する
- この関数と画像検証コードを1つのファイルにまとめます
from flask import Blueprint
api = Blueprint("api_1_0", __name__, url_prefix="/api/v1.0")
from . import demo,verify_code