add phone login
Some checks failed
Build and Deploy Backend / build-and-deploy (push) Failing after 59s

This commit is contained in:
repair-agent 2026-02-09 18:24:58 +08:00
parent 88b8f023f4
commit bc28ef00f1
5 changed files with 107 additions and 18 deletions

View File

@ -23,17 +23,9 @@ class UserDetailSerializer(serializers.ModelSerializer):
class PhoneLoginSerializer(serializers.Serializer):
"""手机号一键登录序列化器"""
phone = serializers.CharField(max_length=20, help_text='手机号')
# 实际项目中应该有验证码或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
"""手机号一键登录序列化器(阿里云号码认证)"""
token = serializers.CharField(help_text='阿里云号码认证 SDK 返回的 accessToken')
class UpdateUserSerializer(serializers.ModelSerializer):

View File

@ -12,6 +12,7 @@ from django.utils import timezone
from utils.response import success, error
from utils.exceptions import ErrorCode
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 apps.admins.authentication import AppJWTAuthentication, AdminJWTAuthentication
from apps.admins.permissions import IsAdminUser
@ -51,27 +52,34 @@ class AuthViewSet(viewsets.ViewSet):
@action(detail=False, methods=['post'], url_path='phone-login')
def phone_login(self, request):
"""
手机号一键登录
手机号一键登录阿里云号码认证
POST /api/v1/auth/phone-login
App 端通过阿里云 SDK 获取 token后端用 token 换取真实手机号
"""
serializer = PhoneLoginSerializer(data=request.data)
if not serializer.is_valid():
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(
phone=phone,
defaults={'nickname': f'用户{phone[-4:]}'}
)
if not user.is_active:
return error(code=101, message='账号已被禁用')
# 生成JWT Token带user_type='app'标识)
tokens = get_app_tokens(user)
return success(data={
'user': UserSerializer(user).data,
'token': tokens,

View File

@ -180,6 +180,12 @@ ALIYUN_SMS = {
'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
SPECTACULAR_SETTINGS = {
'TITLE': 'RTC API',

View File

@ -28,3 +28,4 @@ sqlparse==0.5.5
urllib3==2.6.3
drf-spectacular==0.27.1
alibabacloud_dysmsapi20170525>=4.4.0
alibabacloud_dypnsapi20170525>=3.0.0

82
utils/phone_auth.py Normal file
View 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()