立即注册
luffy值短信登录接口、注册接口
复习
# 1 pycharm 操作git
# fetch和pull的区别 :fetch+merge=pull
# 2 给开源项目贡献代码
# fork代码---》clone下来改就行了---》提交pr---》同意--》贡献者
# 3 登录注册 前端页面
# 4 5个接口
-多方式登录 #手机号/邮箱/用户名
-手机号是否存在 #验证手机号是否在数据库存在
-发送短信 #点发送短信
-短信登录 #短信登录
-短信注册 #短信注册
# 5 手机号是否存在
try:
pass
except:
raise
return Response()
# 6 多方式登录
-前端数据:{username:用户名/手机号/邮箱,password:密码}
-序列化类中---》视图类中调用 ser.is_valid--->执行字段自己的规则,局部钩子,全局钩子---》登录(通过正则匹配),查出用户,校验密码是否正确--->通过用户签发token---》context---》头像处理(imageField)
今日内容
1 腾讯云发送短信二次封装
# 腾讯云短信---》https://console.cloud.tencent.com/smsv2/csms-template
# 步骤:
-创建签名
-创建模板
-发送短信 # 用代码写个方法发送短信 调用他的接口 他给某个手机发短信
# API和SDK
-api接口:http接口
其实就是别人已经写好的可以实现特定功能的函数,而你只需要根据他提供好的接口,也就是调用他的方法,传入他规定的参数,然后这个函数就会帮你实现这些功能。
-sdk:不同语言对api的封装,更方便的使用提供的服务 #软件开发工具包 底层也是封装了api
# 腾讯云短信的sdk
-老版本:2.0 :只针对于发短信(qcloudsms_py):https://cloud.tencent.com/document/product/382/11672
-新版本:3.0:不仅仅针对于发短信(tencentcloud-sdk-python):https://cloud.tencent.com/document/product/382/43196
# 注意:python3.8以上,2.0用起来会有问题, 所以直接用3.0
# 原因用了内置的json模块
# 如何解决
# json:3.6以前是不能序列化bytes格式的
libs/sms/settings.py
APP_ID = ''
APP_KEY = ''
TEMPLATE_ID = ''
SMS_SIGN = ''
SECRET_ID = ''
SECRET_KEY = ''
libs/sms/init.py
from .v2 import send_sms as send_sms_v2
from .v3 import send_sms as send_sms_v3
from .code import gen_code
libs/sms/code.py
import random
# 生成4,或5位的验证码
def gen_code(length=4):
code_str = ''
for i in range(length):
code_str += str(random.randrange(0, 9)) # python 是动态强类型,所以需要转换
return code_str
libs/sms/v2.py
from qcloudsms_py import SmsSingleSender
from qcloudsms_py.httpclient import HTTPError
from . import settings
from utils.logging import logger
def send_sms(phone, code, exp=2):
ssender = SmsSingleSender(settings.APP_ID, settings.APP_KEY)
params = [code, exp]
try:
result = ssender.send_with_param(86, phone, settings.TEMPLATE_ID, params, sign=settings.SMS_SIGN, extend="",
ext="")
if result['result'] == 0:
return True
else:
logger.error('发送短信失败,失败原因为:%s' % result['errmsg'])
return False
except Exception as e:
logger.error('发送短信失败,失败原因为:%s' % str(e))
return False
libs/sms/v3.py
from . import settings
def send_sms(phone, code, exp='2'):
# -*- coding: utf-8 -*-
from tencentcloud.common import credential
from tencentcloud.common.exception.tencent_cloud_sdk_exception import TencentCloudSDKException
# 导入对应产品模块的client models。
from tencentcloud.sms.v20210111 import sms_client, models
# 导入可选配置类
from tencentcloud.common.profile.client_profile import ClientProfile
from tencentcloud.common.profile.http_profile import HttpProfile
try:
cred = credential.Credential(settings.SECRET_ID, settings.SECRET_KEY)
httpProfile = HttpProfile()
httpProfile.reqMethod = "POST" # post请求(默认为post请求)
httpProfile.reqTimeout = 30 # 请求超时时间,单位为秒(默认60秒)
httpProfile.endpoint = "sms.tencentcloudapi.com" # 指定接入地域域名(默认就近接入)
# 非必要步骤:
# 实例化一个客户端配置对象,可以指定超时时间等配置
clientProfile = ClientProfile()
clientProfile.signMethod = "TC3-HMAC-SHA256" # 指定签名算法
clientProfile.language = "en-US"
clientProfile.httpProfile = httpProfile
# 实例化要请求产品(以sms为例)的client对象
# 第二个参数是地域信息,可以直接填写字符串ap-guangzhou,支持的地域列表参考 https://cloud.tencent.com/document/api/382/52071#.E5.9C.B0.E5.9F.9F.E5.88.97.E8.A1.A8
client = sms_client.SmsClient(cred, "ap-guangzhou", clientProfile)
# 实例化一个请求对象,根据调用的接口和实际情况,可以进一步设置请求参数
# 你可以直接查询SDK源码确定SendSmsRequest有哪些属性可以设置
# 属性可能是基本类型,也可能引用了另一个数据结构
# 推荐使用IDE进行开发,可以方便的跳转查阅各个接口和数据结构的文档说明
req = models.SendSmsRequest()
# 基本类型的设置:
# SDK采用的是指针风格指定参数,即使对于基本类型你也需要用指针来对参数赋值。
# SDK提供对基本类型的指针引用封装函数
# 帮助链接:
# 短信控制台: https://console.cloud.tencent.com/smsv2
# sms helper: https://cloud.tencent.com/document/product/382/3773
# 短信应用ID: 短信SdkAppId在 [短信控制台] 添加应用后生成的实际SdkAppId,示例如1400006666
req.SmsSdkAppId = settings.APP_ID
# 短信签名内容: 使用 UTF-8 编码,必须填写已审核通过的签名,签名信息可登录 [短信控制台] 查看
req.SignName = settings.SMS_SIGN
# 短信码号扩展号: 默认未开通,如需开通请联系 [sms helper]
req.ExtendCode = ""
# 用户的 session 内容: 可以携带用户侧 ID 等上下文信息,server 会原样返回
req.SessionContext = "xxx"
# 国际/港澳台短信 senderid: 国内短信填空,默认未开通,如需开通请联系 [sms helper]
req.SenderId = ""
# 下发手机号码,采用 E.164 标准,+[国家或地区码][手机号]
# 示例如:+8613711112222, 其中前面有一个+号 ,86为国家码,13711112222为手机号,最多不要超过200个手机号
req.PhoneNumberSet = ["+86" + phone, ]
# 模板 ID: 必须填写已审核通过的模板 ID。模板ID可登录 [短信控制台] 查看
req.TemplateId = settings.TEMPLATE_ID
# 模板参数: 若无模板参数,则设置为空
req.TemplateParamSet = [code, exp]
# 通过client对象调用DescribeInstances方法发起请求。注意请求方法名与请求对象是对应的。
# 返回的resp是一个DescribeInstancesResponse类的实例,与请求对象对应。
resp = client.SendSms(req)
# 输出json格式的字符串回包
import json
res_dict = json.loads(resp.to_json_string(indent=2))
print(res_dict)
if res_dict['SendStatusSet'][0]['Code'] == 'Ok':
return True
else:
return False
except TencentCloudSDKException as err:
print(err)
return False
2 发送短信(验证码)接口
class UserView(ViewSet):
@action(methods=['GET'], detail=False)
def send_sms(self, request):
from libs.sms import gen_code, send_sms_v3
# 从请求url获取phone
phone = request.query_params.get('phone')
#调用gen_code获得随机字符串
sms_code = gen_code()
#验证码不只是需要发送还需要保存起来 放到缓存,默认放到内存中
#设置缓存
cache.set(settings.SMS_CODE_CACHE%phone,sms_code)
# 调用方法获得发送短信
res = send_sms_v3(phone, sms_code)
print(sms_code)
if res:
return APIResponse()
else:
return APIResponse(code=101, msg='发送失败,请稍后再试')
3 短信登录接口
# 前端传入(post):{mobile:1892322345,code:1234}
界面如下
3.1 serializer.py
class MobileSerializer(serializers.ModelSerializer):
# code字段不是表字段,需要重写 他是验证码字段
code = serializers.CharField(max_length=5, min_length=4)
mobile= serializers.CharField()
class Meta:
model = User
# 往里走--->反序列化校验:username和password---》只是校验长短
# 往外走--->序列化---》username,icon
fields = ['mobile', 'code']
# 不需要序列化 所以不用这个
# extra_kwargs = {
# 'password': {'write_only': True},
# 'icon': {'read_only': True}
# }
def validate(self, attrs):
# 1.检验code是否正确
self._check_code(attrs)
# 2.根据手机号查用户
user=self._get_user_by_mobile(attrs)
# 3.签发token
token=_get_token(user)
# 4.把token 放入到当前对象中给view用
self.context['token'] = token
self.context['username'] = user.username
# self.context['icon']=user.icon # icon的有点问题
# 这个地址是服务端地址,服务端地址从request对象中可以取出request.META['HTTP_HOST']
request = self.context.get('request')
self.context['icon'] = 'http://%s/media/' % request.META['HTTP_HOST'] + str(user.icon)
return attrs
#检查code验证码是否正确
def _check_code(self, attrs):
# 发送验证码接口还要存一份验证码 和注册的手机号 方便后期验证
code = attrs.get('code')
# 取出该手机号对应的code
mobile = attrs.get('mobile')
old_code = cache.get(settings.SMS_CODE_CACHE % mobile)
#比较code
if not old_code==code:
raise ValidationError('验证码错误')
#判断用户是否存在
def _get_user_by_mobile(self,attrs):
mobile=attrs.get('mobile')
user=User.objects.filter(mobile=mobile).first()
if user:
return user
else:
raise ValidationError('用户不存在')
#由于token获取经常用到拿出来写一个方法需要就调用一下减少了代码冗余
def _get_token(user):
# 根据user获取payload
payload = jwt_payload_handler(user)
# 根据payload得到token
token = jwt_encode_handler(payload);
return token
3.2 views.py
class LoginView(GenericViewSet):
queryset = User.objects.all()
serializer_class = LoginSerializer
#重写get_serializer_class方法 get serializer从这里取值
def get_serializer_class(self):
print(self.action)
if self.action == 'mul_login':
return self.LoginSerializer
else:
return MobileSerializer
@action(methods=['POST'], detail=False, ) # user/login/mobile_login
def mobile_login(self, request):
return self._login(request)
# 代码冗余抽出来写成一个方法
def _login(self, request):
try:
# 校验规则和签发token都写到序列化类中,获取序列化类获取的不对 所以需要
ser = self.get_serializer(data=request.data, context={'request': request})
# 方式一
# ser=MobileSerializer(data=request.data, context={'request': request})
# 方式一 重写get_serializer
ser.is_valid(raise_exception=True) # 走序列化类的字段自己的规则,局部钩子和全局钩子
token = ser.context.get('token')
username = ser.context.get('username')
icon = ser.context.get('icon')
except Exception as e:
raise APIException(str(e))
return APIResponse(token=token, username=username, icon=icon)
3.3 cache的使用
from django.core.cache import cache
cache.set(key,value) #存
cache.get(key) #取
4 短信注册接口
# 前端传入的 {mobile:1234,code:1234,password:1234} 手机号 验证码 密码(这个有的网站没有)
# 自动给他设置一个密码 或者没有密码可以登陆之后修改密码
# 新增--》User表中插入数据,username唯一,干脆手机号就是用户名
界面如下
4.1 serializer.py
class RegisterSerializer(serializers.ModelSerializer,Code_Token):
# code 字段不存在 需要重写 不然
code = serializers.CharField(write_only=True)
class Meta:
model = User
fields = ['mobile', 'code', 'password', 'username']
extra_kwargs = {
'username': {'read_only': True},
'password': {'write_only': True}
}
def validate(self, attrs):
# 1.校验手机号是否合法 没有必要去校验 因为如果手机号不合法 他在缓存取出code一对比就失败了code就已经不合法了,写上也没问题
# 2.验证code
self._check_code(attrs)
# 3.创建出username 不然怎么存数据库
# attrs{'mobile':xxx,'password':123,'username':xxx,'code':1234}
attrs['username'] = attrs.get('mobile')
# 4.踢出code 因为code不是表字段
attrs.pop('code')
return attrs
# 因为在父类里 save方法触发 如果是新增触发create 修改触发update 所以自己需要重写create
# validated_data 校验成功的数据
def create(self, validated_data):
# 重写目的是因为密码是秘文 传过来的是明文 需要使用create_user
user = User.objects.create_user(**validated_data)
print(user)
return user
4.2 views.py
# 短信注册接口 继承GenericViewSet为了自动生成路由 CreateModelMixin为了create方法 写入数据库
class RegisterView(GenericViewSet,CreateModelMixin):
# 数据库
queryset = User.objects.all()
# 序列化类
serializer_class = RegisterSerializer
# 重写create
def create(self, request, *args, **kwargs):
# 父类返回的是return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)
# 父类create里 self.perform_create(serializer) 会调用save方法
# save方法 会触发 新增触发create 修改触发update方法 所以序列化类重写create
res = super().create(request, *args, **kwargs)
print(res)
# data=父类的serializer.data
return APIResponse(msg='注册成功')
# data是一个字典 有mobile password等
5 登录注册前端完成(账号密码登录和短信验证码方式登录)
前端存的三个位置
1 cookie中
2 localStorage 3 sessionStorage
cookie 可以设置过期时间,
需要借助于第三方模块,vue-cookies
-cnpm install vue-cookies
-main.js中 设置
import cookies from 'vue-cookies'
Vue.prototype.$cookies = cookies;
localStorage:永久存储
sessionStorage:关闭浏览器就失效 清空
5.1 Login.vue
登录
{{ sms_interval }}
登录
5.2 Header.vu
老男孩IT教育 | 帮助有志向的年轻人通过努力学习获得体面的工作和生活