add phone login
Some checks failed
Build and Deploy Backend / build-and-deploy (push) Failing after 59s
Some checks failed
Build and Deploy Backend / build-and-deploy (push) Failing after 59s
This commit is contained in:
parent
88b8f023f4
commit
bc28ef00f1
@ -23,17 +23,9 @@ class UserDetailSerializer(serializers.ModelSerializer):
|
|||||||
|
|
||||||
|
|
||||||
class PhoneLoginSerializer(serializers.Serializer):
|
class PhoneLoginSerializer(serializers.Serializer):
|
||||||
"""手机号一键登录序列化器"""
|
"""手机号一键登录序列化器(阿里云号码认证)"""
|
||||||
|
|
||||||
phone = serializers.CharField(max_length=20, help_text='手机号')
|
token = serializers.CharField(help_text='阿里云号码认证 SDK 返回的 accessToken')
|
||||||
# 实际项目中应该有验证码或token
|
|
||||||
# code = serializers.CharField(max_length=10, help_text='验证码')
|
|
||||||
|
|
||||||
def validate_phone(self, value):
|
|
||||||
# 简单验证手机号格式
|
|
||||||
if not value.isdigit() or len(value) != 11:
|
|
||||||
raise serializers.ValidationError('手机号格式不正确')
|
|
||||||
return value
|
|
||||||
|
|
||||||
|
|
||||||
class UpdateUserSerializer(serializers.ModelSerializer):
|
class UpdateUserSerializer(serializers.ModelSerializer):
|
||||||
|
|||||||
@ -12,6 +12,7 @@ from django.utils import timezone
|
|||||||
from utils.response import success, error
|
from utils.response import success, error
|
||||||
from utils.exceptions import ErrorCode
|
from utils.exceptions import ErrorCode
|
||||||
from utils.sms import send_sms_code, verify_sms_code
|
from utils.sms import send_sms_code, verify_sms_code
|
||||||
|
from utils.phone_auth import get_phone_auth_client
|
||||||
from utils.oss import get_oss_client
|
from utils.oss import get_oss_client
|
||||||
from apps.admins.authentication import AppJWTAuthentication, AdminJWTAuthentication
|
from apps.admins.authentication import AppJWTAuthentication, AdminJWTAuthentication
|
||||||
from apps.admins.permissions import IsAdminUser
|
from apps.admins.permissions import IsAdminUser
|
||||||
@ -51,14 +52,21 @@ class AuthViewSet(viewsets.ViewSet):
|
|||||||
@action(detail=False, methods=['post'], url_path='phone-login')
|
@action(detail=False, methods=['post'], url_path='phone-login')
|
||||||
def phone_login(self, request):
|
def phone_login(self, request):
|
||||||
"""
|
"""
|
||||||
手机号一键登录
|
手机号一键登录(阿里云号码认证)
|
||||||
POST /api/v1/auth/phone-login
|
POST /api/v1/auth/phone-login
|
||||||
|
App 端通过阿里云 SDK 获取 token,后端用 token 换取真实手机号
|
||||||
"""
|
"""
|
||||||
serializer = PhoneLoginSerializer(data=request.data)
|
serializer = PhoneLoginSerializer(data=request.data)
|
||||||
if not serializer.is_valid():
|
if not serializer.is_valid():
|
||||||
return error(message=str(serializer.errors))
|
return error(message=str(serializer.errors))
|
||||||
|
|
||||||
phone = serializer.validated_data['phone']
|
access_token = serializer.validated_data['token']
|
||||||
|
|
||||||
|
# 用 token 换取真实手机号
|
||||||
|
client = get_phone_auth_client()
|
||||||
|
phone, err = client.get_mobile(access_token)
|
||||||
|
if not phone:
|
||||||
|
return error(code=102, message=err or '号码认证失败')
|
||||||
|
|
||||||
# 获取或创建用户
|
# 获取或创建用户
|
||||||
user, created = User.objects.get_or_create(
|
user, created = User.objects.get_or_create(
|
||||||
|
|||||||
@ -180,6 +180,12 @@ ALIYUN_SMS = {
|
|||||||
'SEND_INTERVAL': 60, # 发送间隔(秒) 60秒
|
'SEND_INTERVAL': 60, # 发送间隔(秒) 60秒
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Aliyun Phone Auth (号码认证/一键登录)
|
||||||
|
ALIYUN_PHONE_AUTH = {
|
||||||
|
'ACCESS_KEY_ID': os.environ.get('PHONE_AUTH_ACCESS_KEY_ID', ALIYUN_ACCESS_KEY_ID),
|
||||||
|
'ACCESS_KEY_SECRET': os.environ.get('PHONE_AUTH_ACCESS_KEY_SECRET', ALIYUN_ACCESS_KEY_SECRET),
|
||||||
|
}
|
||||||
|
|
||||||
# Swagger/OpenAPI Settings
|
# Swagger/OpenAPI Settings
|
||||||
SPECTACULAR_SETTINGS = {
|
SPECTACULAR_SETTINGS = {
|
||||||
'TITLE': 'RTC API',
|
'TITLE': 'RTC API',
|
||||||
|
|||||||
@ -28,3 +28,4 @@ sqlparse==0.5.5
|
|||||||
urllib3==2.6.3
|
urllib3==2.6.3
|
||||||
drf-spectacular==0.27.1
|
drf-spectacular==0.27.1
|
||||||
alibabacloud_dysmsapi20170525>=4.4.0
|
alibabacloud_dysmsapi20170525>=4.4.0
|
||||||
|
alibabacloud_dypnsapi20170525>=3.0.0
|
||||||
|
|||||||
82
utils/phone_auth.py
Normal file
82
utils/phone_auth.py
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
"""
|
||||||
|
阿里云号码认证服务工具类
|
||||||
|
用于一键登录:App 端获取 token → 后端用 token 换取真实手机号
|
||||||
|
"""
|
||||||
|
import logging
|
||||||
|
from django.conf import settings
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
try:
|
||||||
|
from alibabacloud_dypnsapi20170525.client import Client
|
||||||
|
from alibabacloud_tea_openapi.models import Config
|
||||||
|
from alibabacloud_dypnsapi20170525.models import GetMobileRequest
|
||||||
|
PHONE_AUTH_SDK_AVAILABLE = True
|
||||||
|
except ImportError:
|
||||||
|
PHONE_AUTH_SDK_AVAILABLE = False
|
||||||
|
|
||||||
|
|
||||||
|
class PhoneAuthClient:
|
||||||
|
"""阿里云号码认证客户端(单例)"""
|
||||||
|
|
||||||
|
_instance = None
|
||||||
|
|
||||||
|
def __new__(cls):
|
||||||
|
if cls._instance is None:
|
||||||
|
cls._instance = super().__new__(cls)
|
||||||
|
cls._instance._initialized = False
|
||||||
|
return cls._instance
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
if self._initialized:
|
||||||
|
return
|
||||||
|
|
||||||
|
if not PHONE_AUTH_SDK_AVAILABLE:
|
||||||
|
self.client = None
|
||||||
|
self._initialized = True
|
||||||
|
return
|
||||||
|
|
||||||
|
auth_config = settings.ALIYUN_PHONE_AUTH
|
||||||
|
if not auth_config.get('ACCESS_KEY_ID'):
|
||||||
|
self.client = None
|
||||||
|
self._initialized = True
|
||||||
|
return
|
||||||
|
|
||||||
|
config = Config(
|
||||||
|
access_key_id=auth_config['ACCESS_KEY_ID'],
|
||||||
|
access_key_secret=auth_config['ACCESS_KEY_SECRET'],
|
||||||
|
endpoint='dypnsapi.aliyuncs.com',
|
||||||
|
)
|
||||||
|
self.client = Client(config)
|
||||||
|
self._initialized = True
|
||||||
|
|
||||||
|
def get_mobile(self, access_token):
|
||||||
|
"""
|
||||||
|
用 App 端 SDK 获取的 token 换取真实手机号
|
||||||
|
:param access_token: App 端 SDK 返回的 token
|
||||||
|
:return: (phone: str|None, error_msg: str)
|
||||||
|
"""
|
||||||
|
if not self.client:
|
||||||
|
logger.warning('号码认证 SDK 未配置')
|
||||||
|
return None, '号码认证服务未配置'
|
||||||
|
|
||||||
|
try:
|
||||||
|
request = GetMobileRequest(access_token=access_token)
|
||||||
|
response = self.client.get_mobile(request)
|
||||||
|
|
||||||
|
if response.body.code == 'OK':
|
||||||
|
mobile = response.body.get_mobile_result_dto.mobile
|
||||||
|
logger.info('号码认证成功: %s', mobile)
|
||||||
|
return mobile, ''
|
||||||
|
else:
|
||||||
|
msg = response.body.message or response.body.code
|
||||||
|
logger.error('号码认证失败: %s', msg)
|
||||||
|
return None, msg
|
||||||
|
except Exception as e:
|
||||||
|
logger.error('号码认证异常: %s', str(e))
|
||||||
|
return None, str(e)
|
||||||
|
|
||||||
|
|
||||||
|
def get_phone_auth_client():
|
||||||
|
"""获取号码认证客户端单例"""
|
||||||
|
return PhoneAuthClient()
|
||||||
Loading…
x
Reference in New Issue
Block a user