立即登录
Django项目--路飞学城项目05
1 腾讯云短信二次封装
tx_sms
__init__.py
settings.py
sms.py
1.1 init.py
from .sms import get_code, send_sms
1.2 settings.py
# 短信应用 SDK AppID
APPPID = # SDK AppID 以1400开头
# 短信应用 SDK AppKey
APPKEY = ""
# 短信模板ID,需要在短信控制台中申请
TEMPLATE_ID = # NOTE: 这里的模板 ID`7839` 只是示例,真实的模板 ID 需要在短信控制台中申请
# 签名
SMS_SIGN = ""
1.3 sms.py
import random
from qcloudsms_py import SmsSingleSender
from qcloudsms_py.httpclient import HTTPError
from .settings import *
from utils.logging import get_logger
logger = get_logger()
# 获取验证码的函数(4位数字)
def get_code():
code_str = ''
for i in range(4):
number = str(random.randint(0, 9))
code_str += number
return code_str
# 发送短信的函数
def send_sms(phone, code):
ssender = SmsSingleSender(APPPID, APPKEY)
params = [code, '1'] # 当模板没有参数时,`params = []`
try:
result = ssender.send_with_param(86, phone, TEMPLATE_ID, params, sign=SMS_SIGN, extend="", ext="")
except Exception as e:
# 记录日志
logger.error('%s手机号,发送短信失败,错误信息是%s' % (phone,str(e)))
if result['result'] == 0:
return True
else:
logger.warning('%s手机号,发送短信失败,失败原因是%s' % (phone, result['errmsg']))
return False
2 短信发送接口
# 前端给我手机号,get请求
2.1 视图
# action装饰器的拓展使用--给同一个视图view下,不同的接口,添加不同的类设置
可用action装饰器 参数:throttle_classes=[SMSThrottle]
class MobileView(ViewSet):
@action(methods=['GET'],detail=False,url_path='sms',throttle_classes=[SMSThrottle])
def send_sms(self, request):
mobile = request.query_params.get('mobile')
if mobile and re.match('^1[3-9][0-9]{9}$', mobile):
# 发送短信
# 获取验证码,记录验证码(放到缓存中,默认还是缓存到内存)
code = tx_sms.get_code()
cache.set(settings.CACHE_SMS % mobile, code)
# 调用发送短信函数
res = tx_sms.send_sms(mobile, code)
if res:
return APIResponse(msg='短信发送成功')
else:
raise APIException('短信发送失败')
else:
raise APIException('手机号不合法')
2.2 路由
# view 采用的自动生成路由
# 这是自己写路由的形式
path('sms/', views.LoginView.as_view({'get':'send_sms'})),
2.3 配合一个频率类
# 限制一个手机号,一分钟只能发一次验证码
# 频率类全局配置
REST_FRAMEWORK = {
'DEFAULT_THROTTLE_RATES': {
'sms': '1/m',
},
}
from rest_framework.throttling import SimpleRateThrottle
class SMSThrottle(SimpleRateThrottle):
scope = 'sms'
def get_cache_key(self, request, view):
mobile = request.query_params.get('mobile')
return mobile
3 短信登录接口
# post, 手机号,验证码
3.1 视图
class LoginView(ViewSet):
def mobile_login(self, request):
# 逻辑写在序列化类中
ser = serializer.MobileLoginSerializer(data=request.data)
ser.is_valid(raise_exception=True) # 执行序列化类字段自己的校验规则,局部钩子,全局钩子
token = ser.context.get('token')
username = ser.context.get('username')
return APIResponse(msg='登录成功', username=username, token=token)
3.2 序列化类
class MobileLoginSerializer(serializers.ModelSerializer):
# 重写它的原因,unique=True
mobile = serializers.CharField()
#code 由于不是表的字段,所有必须重写
code=serializers.CharField()
class Meta:
model = models.User
fields = ['code', 'mobile']
# 用户校验和签发token
def validate(self, attrs):
'''
1 校验code是否正确:从缓存中根据手机号获取code,比较
2 根据手机号,拿到当前用户,
3 根据用户,签发token
4 把token放到序列化类的对象中
'''
user = self._get_user(attrs)
token = self._get_token(user)
# 上下文,是个字典
self.context['token'] = token
self.context['username'] = user.username
return attrs
def _get_user(self, attrs): # 不是私有,但尽量只给内部用
code = attrs.get('code')
mobile = attrs.get('mobile')
# 校验code
cache_code = cache.get(settings.CACHE_SMS % mobile)
# code == "111111" 是防止验证码发送频繁,留的后门
if cache_code and code == cache_code or code == "111111":
user = models.User.objects.filter(mobile=mobile).first()
if not user:
raise ValidationError({'detail': '手机号不存在'})
return user
else:
raise ValidationError({'detail': '验证码错误'})
def _get_token(self, user):
payload = jwt_payload_handler(user)
token = jwt_encode_handler(payload)
return token
3.3 路由
path('mobile_login/', views.LoginView.as_view({'post':'mobile_login'})),
4 短信注册接口
# 手机号 验证码 密码
4.1 视图类
class RegisterView(GenericViewSet, CreateModelMixin):
queryset = User.objects.all()
serializer_class = serializer.RegisterUserSerializer
def create(self, request, *args, **kwargs):
super().create(request, *args, **kwargs)
return APIResponse(msg='注册成功')
4.2 序列化类
class RegisterUserSerializer(serializers.ModelSerializer):
# code 不是User表的字段,必须重写
code = serializers.CharField(max_length=4, min_length=4,write_only=True)
class Meta:
model = models.User
fields = ['mobile', 'code', 'password']
extra_kwargs = {'mobile': {'write_only': True},
'password': {'write_only': True}
}
# 如果想让密码比较复杂,可以给密码写一个局部钩子,使用正则去匹配
# 校验字段
def validate(self, attrs):
'''
1 校验code是否正确
2 把code从attrs中剔除
3 使用手机号作为用户名,后期可以改
'''
code = attrs.get('code')
mobile = attrs.get('mobile')
# 校验code
cache_code = cache.get(settings.CACHE_SMS % mobile)
# code == "111111" 是防止验证码发送频繁,留的后门
if cache_code and code == cache_code or code == "111111":
# 把code剔除,code不是User表的字段,不能用来保存
attrs.pop('code')
# 创造一个username
attrs['username'] = mobile
else:
raise ValidationError({'detail': '验证码错误'})
return attrs
# 为什么要重写create? 因为密码是明文的,需要使用create_user创建用户
def create(self, validated_data):
user = models.User.objects.create_user(**validated_data)
return user
5 前端登录注册
# 多方式登录接口
# 校验手机号是否存在接口
# 发送验证码接口(腾讯短信平台)
# 验证码登录接口
# 验证码注册接口
5.1 Login.vue
老男孩IT教育 | 帮助有志向的年轻人通过努力学习获得体面的工作和生活
5.2 Register.vue
{{ sms_interval }}
注册
5.3 Header.vue
老男孩IT教育 | 帮助有志向的年轻人通过努力学习获得体面的工作和生活