# 使用第三方短信接口 # 注册一个个人公众号---》实名认证 -需要没有注册过的邮箱 # 使用腾讯云短信 1 创建短信签名 -选择公众号截图 2 创建短信正文模版 -创建一个模板 3 等待审核 4 发送短信---》代码发送 -sdk v2 老一些 v3 新的 # 什么是api接口,什么是sdk -SDK一般指软件开发工具包,第三方平台提供的通过相应语言写的包 -咱们只需要集成到项目中, 包.方法 -使用简单 -问题是:有的语言没有sdk -api接口:发送http请求,携带相关数据,调用,通用跟语言没关系
# 腾讯云官方 -短信申请:申请签名,申请模板 -使用官方提供的sdk或者api发送短信 -如果没有sdk就是用api调用 -现在有sdk,优先使用sdk,简单---》不同语言封装的包,直接调用包的方法就可以完成相关操作 - 2.0版本的sdk,3.0版本的sdk -安装3.0的sdk:pip install tencentcloud-sdk-python # 腾讯云所有的功能都集成到这个包中了 -使用:https://cloud.tencent.com/document/product/382/43196#.E9.80.9A.E8.BF.87-pip-.E5.AE.89.E8.A3.85.EF.BC.88.E6.8E.A8.E8.8D.90.EF.BC.89 -安装2.0的sdk:pip install qcloudsms_py # 腾讯云短信功能在这个包中 -使用:https://cloud.tencent.com/document/product/382/11672
init.py
from .sms import get_code,send_sms_v2
settings.py
# 短信应用 SDK AppID APPID = # SDK AppID 以1400开头 # 短信应用 SDK AppKey APPKEY = "" # 需要发送短信的手机号码 # 短信模板ID,需要在短信控制台中申请 TEMPLATE_ID = 1470213 # NOTE: 这里的模板 ID`7839` 只是示例,真实的模板 ID 需要在短信控制台中申请 # 签名 SMS_SIGN = "咋啦叭呼" # NOTE: 签名参数使用的是`签名内容`,而不是`签名ID`。这里的签名"腾讯云"只是示例,真实的签名需要在短信控制台中申请
sms.py
# 生成随机n位验证码的函数 import random from qcloudsms_py import SmsSingleSender from qcloudsms_py.httpclient import HTTPError from . import settings # 使用相对导入 def get_code(n=4): code = '' for i in range(n): code += str(random.randint(0, 9)) return code # 发送短信的函数 def send_sms(phone, code): phone_numbers = [phone, ] ssender = SmsSingleSender(settings.APPID, settings.APPKEY) params = [code, '1'] # 当模板没有参数时,`params = []` try: result = ssender.send_with_param(86, phone_numbers[0], settings.TEMPLATE_ID, params, sign=settings.SMS_SIGN, extend="", ext="") except Exception as e: return False return True if __name__ == '__main__': print(get_code())
init.py
from .sms import get_code,send_sms_v2
settings.py
SECRET_ID='AKIDZL2jO2WtBOWaXPE5qV9iKtPvRrCQZiiY' SECRET_KEY='kKl5FF6oNvLZaR5WklZAZllY9XkneIl2' APPID = '1400705789' # SDK AppID 以1400开头 TEMPLATE_ID = '1470213' # 签名 SMS_SIGN = "咋啦叭呼" ### v3版本的APPID TEMPLATE_ID 都必须使用str类型,数字类型报错
sms.py
# 生成随机n位验证码的函数 import random from qcloudsms_py import SmsSingleSender from qcloudsms_py.httpclient import HTTPError from . import settings # 使用相对导入 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 from . import settings def get_code(n=4): code = '' for i in range(n): code += str(random.randint(0, 9)) return code # 发送短信的函数 def send_sms(phone, code): try: cred = credential.Credential(settings.SECRET_ID, settings.SECRET_KEY) httpProfile = HttpProfile() httpProfile.reqMethod = "POST" httpProfile.reqTimeout = 30 httpProfile.endpoint = "sms.tencentcloudapi.com" clientProfile = ClientProfile() clientProfile.signMethod = "TC3-HMAC-SHA256" clientProfile.language = "en-US" clientProfile.httpProfile = httpProfile client = sms_client.SmsClient(cred, "ap-guangzhou", clientProfile) req = models.SendSmsRequest() req.SmsSdkAppId = settings.APPID req.SignName = settings.SMS_SIGN req.TemplateId = settings.TEMPLATE_ID req.TemplateParamSet = [code, '5'] req.PhoneNumberSet = ["+86" + phone, ] req.SessionContext = "" req.ExtendCode = "" req.SenderId = "" resp = client.SendSms(req) # print(resp.to_json_string(indent=2)) return True except TencentCloudSDKException as err: # print(err) return False
# post get 我选择用get # 前端传入手机号,调用发送短信函数,完成发送短信
路由
# 127.0.0.1:8000/api/v1/user/sms/send_sms router.register('sms', SMSView, 'sms')
视图类
# from libs import tencent_sms_v2 as tencent_sms from libs import tencent_sms_v3 as tencent_sms import re class SMSView(ViewSet): @action(methods=['GET'], detail=False) def send_sms(self, request): mobile = request.query_params.get('mobile') if mobile and re.match(r'^1[3-9][0-9]{9}$', mobile): code = tencent_sms.get_code() res = tencent_sms.send_sms(mobile, code) if res: return APIResponse() else: raise APIException(detail='发送短信失败') else: raise APIException(detail='手机号有误')
# 前端传入的格式---{mobile:12334455,code:8888}
路由
router.register('sms', SMSView, 'sms')
视图类
class LoginView(GenericViewSet): queryset = User.objects.all() serializer_class = MulLoginSerializer def common_login(self, request): ser = self.get_serializer(data=request.data, context={'request': request}) ser.is_valid(raise_exception=True) token = ser.context.get('token') icon = ser.context.get('icon') return APIResponse(token=token, icon=icon) # 重写这个方法get_serializer_class,返回什么序列化类,当前用的序列化类就是哪个 def get_serializer_class(self): # 方式一 :通过请求路径来判断,可以 # if 'mul_login' in self.request.path: # return self.serializer_class # else: # return 序列化类 # 方式二:通过action判断 if self.action =='sms_login': return SMSLoginSerializer else: return self.serializer_class @action(methods=['POST'], detail=False) def mul_login(self, request): return self.common_login(request) @action(methods=['POST'], detail=False) def sms_login(self, request): return self.common_login(request)
序列化类
class SMSLoginSerializer(serializers.ModelSerializer): code = serializers.CharField() # code不是User表的字段,所以要重写code class Meta: model = User fields = ['mobile', 'code'] def _check_user(self, attrs): mobile = attrs.get('mobile') code = attrs.get('code') # 校验code对不对?从缓存中取出来 old_code = cache.get('sms_cache_%s' % mobile) # 取出来,立马失效 cache.set('sms_cache_%s' % mobile,'') if old_code == code: # 万能验证码 user = User.objects.filter(mobile=mobile).first() if user: return user else: raise APIException(detail='用户不存在') else: raise APIException(detail='验证码错误') def _get_token(self, user): payload = jwt_payload_handler(user) token = jwt_encode_handler(payload) return token def validate(self, attrs): # 在全局钩子中,校验用户是否登录成功,如果登录成功直接签发token # 1 手机号得到user user = self._check_user(attrs) # 2 user签发token---》签发token token = self._get_token(user) # 3 把token放到 self 对象中得context属性中 self.context['token'] = token host = self.context.get('request').META['HTTP_HOST'] self.context['icon'] = 'http://%s/media/%s' % (host, str(user.icon)) return attrs
# 前端传入数据----》{mobile:1983334,password:123,code:8888}
视图类
class UserView(GenericViewSet, CreateModelMixin): serializer_class = UserSerializer queryset = User.objects.all() def create(self, request, *args, **kwargs): super().create(request, *args, **kwargs) return APIResponse(msg='注册成功')
序列化类
class UserSerializer(serializers.ModelSerializer): code = serializers.CharField(write_only=True) # code不是User表的字段,所以要重写code class Meta: model = User fields = ['mobile', 'password', 'code'] extra_kwargs = {'password': {'write_only': True}} def validate(self, attrs): # 1 校验code是否正确 mobile = attrs.get('mobile') code = attrs.get('code') old_code = cache.get('sms_cache_%s' % mobile) if not code == old_code: raise APIException(detail='验证码错误') # 2 手机号是否被注册过 if User.objects.filter(mobile=mobile).first(): raise APIException(detail='该手机号已经被注册') # 3 入库前准备---》code字段从attrs中剔除,username必填,手机号就是用户名 attrs.pop('code') attrs['username'] = mobile return attrs def create(self, validated_data): # 因为新增用户,是用create_user新增的,不是使用create新增的 user = User.objects.create_user(**validated_data) return user
路由
router.register('register', UserView, 'register')