認証コードの送信インターフェースと公式SMS SDKのカスタマイズ

目次- 認証

  • 認証コード送信API
  • 公式SMS SDKのカスタマイズ

認証

  1. データベーステーブルを設計 2. 要求に応じてAPIを設計 3. コードを記述してAPIの機能を実装
  • ユーザー名、メールアドレス、電話番号によるパスワードログイン

    1. ユーザー名とパスワードを入力し、ログインAPIにPOSTリクエストを送る
    2. 検証が成功した場合、トークンを発行する
  • 電話番号と認証コードによるログイン

    1. 電話番号を入力
    2. 「認証コードを送信」ボタンをクリックし、認証コード送信APIにGETリクエストを送る
    3. 認証コードを入力し、電話番号と認証コードによるログインAPIにPOSTリクエストを送る
    4. Djangoキャッシュから認証コードを取り出し検証を行い、検証が成功するとトークンを発行する
    '''
    # ...\luffyapi\luffyapi\apps\user\urls.py
    ...
    urlpatterns = [
        ...,
        path('code/login/', views.CodeLoginAPIView.as_view()),  # 電話番号 + 認証コードログインAPI
    ]
    
    
    # ...\luffyapi\luffyapi\apps\user\views.py
    from rest_framework_jwt.views import JSONWebTokenAPIView
    from . import my_serializers
    
    
    class CodeLoginAPIView(JSONWebTokenAPIView):
        serializer_class = my_serializers.CodeLoginSerializer
    
    
    # ...\luffyapi\luffyapi\apps\user\my_serializers.py
    ...
    from rest_framework import serializers
    
    
    class CodeLoginSerializer(serializers.Serializer):
        code = serializers.CharField(min_length=6, max_length=6)
        mobile = serializers.CharField(min_length=11, max_length=11)
    
        def validate(self, attrs):
            mobile = attrs.get('mobile')
            code = attrs.get('code')
    
            if not code.isdigit():  # 認証コードが数字のみかチェックし、それ以外はエラー
                raise serializers.ValidationError('認証コードは数字のみでなければなりません')
            cache_code = cache.get(settings.CODE_CACHE_FORMAT % mobile)  # サーバー側のキャッシュから認証コードを取得
            if code != cache_code:
                raise serializers.ValidationError('認証コードが間違っています')
    
            try:
                user = models.User.objects.get(mobile=mobile, is_active=True)
            except:
                raise serializers.ValidationError('この電話番号は登録されていません')
    
            payload = jwt_payload_handler(user)  
    
            token = jwt_encode_handler(payload) 
    
            self.object = {'token': token, 'user': user}  
    
            cache.delete(settings.CODE_CACHE_FORMAT % mobile)  # 電話番号 + 認証コードログイン成功後にDjangoキャッシュ内の認証コードを削除 
    
            return attrs
    '''
    
    

認証コード送信API

  1. 認証コードを生成
  2. 腾讯云に認証コードを送信
  3. 腾讯云のSMS APIドキュメントを参照
  4. 公式提供されたSMS SDKをカスタマイズ
  5. SMS送信成功後に生成された認証コードをDjangoキャッシュに保存
'''
# ...\luffyapi\luffyapi\apps\user\urls.py
...
urlpatterns = [
    ...
    path('code/send/', views.SendSmsAPIView.as_view()),  # 認証コード送信API
]


# ...\luffyapi\luffyapi\apps\user\views.py
...
from luffyapi.libs.sms_sdk import send_sms
from luffyapi.utils.sms_code import generate_code
from django.core.cache import cache
from django.conf import settings
from rest_framework.response import Response


class SendSmsAPIView(APIView):
    authentication_classes = []
    permission_classes = []

    def get(self, request, *args, **kwargs):  # GETリクエストの処理効率はPOSTより高い
        mobile = request.query_params.get('mobile')
        if not mobile:
            return Response(data={'code': 1001, 'detail': 'mobileフィールドが必要です'}, status=400)
        if not re.match(r'^1[3-9][0-9]{9}$', mobile):
            return Response(data={'code': 1002, 'detail': '電話番号の形式が誤っています'}, status=400)

        code = generate_code()

        res = send_sms([mobile, ], code, settings.CODE_EXP // 60)  # 認証コードの有効時間は分単位
        if not res:
            return Response(data={'code': 1003, 'detail': '認証コード送信失敗'}, status=500)

        cache.set(settings.CODE_CACHE_FORMAT % mobile, code, settings.CODE_EXP)  # Djangoキャッシュの有効時間は秒単位

        return Response(data={'code': 0, 'msg': '認証コード送信成功', })
        
        
# ...\luffyapi\luffyapi\utils\sms_code.py
# 6桁のランダムな数字の認証コードを生成
def generate_code():
    import random
    lt = []
    for i in range(10):
        lt.append(str(i))
    rand_lt = random.sample(lt, 6)
    code = ''.join(rand_lt)
    return code


# ...\luffyapi\luffyapi\settings\const_settings.py
...
CODE_CACHE_FORMAT = 'code_cache_%s'  # 認証コードキャッシュキー

CODE_EXP = 30000  # 認証コード有効時間
'''

公式提供されたSMS SDKのカスタマイズ

'''
# ...\luffyapi\luffyapi\libs\sms_sdk\settings.py
...  # 認証コード送信メソッドを呼び出すために必要な固定設定


# ...\luffyapi\luffyapi\libs\sms_sdk\single_send.py
from qcloudsms_py import SmsSingleSender
from .settings import TEMPLATE_ID, SMS_SIGN, APPID, APPKEY
from luffyapi.utils.my_logging import logger


# 認証コードを送信
def send_sms(phone_num_lt, code, exp):
    try:
        sender = SmsSingleSender(APPID, APPKEY)
        response = sender.send_with_param(86, phone_num_lt[0], TEMPLATE_ID, (code, exp), sign=SMS_SIGN, extend="", ext="")
        # print(response)  # {'result': 0, 'errmsg': 'OK', 'ext': '', 'sid': '8:rIcypgwqRVSqRCVVJ0e20200113', 'fee': 1}

        if response.get('result') != 0:
            logger.error('認証コード送信失敗, "code": %s, "detail": %s' % (response.get('result'), response.get('errmsg')))
            return False
        return True

    except Exception as e:
        logger.error('コード実行異常, "detail": %s' % e)
        

# ...\luffyapi\luffyapi\libs\sms_sdk\__init__.py
from .single_send import send_sms  # パッケージ向けカスタマイズ
'''

タグ: restful JWT Python Django Caching

6月3日 22:55 投稿