luffy-08

2023-05-16

这里写目录标题

  • 一.集成了腾讯短信(封装)
    • luffyapi/lib/t_sms/settings.py
    • luffyapi/lib/t_sms/sms.py
  • 二.短信验证码接口
    • 2.1路由,配置
    • 2.2视图
  • 三.短信登录接口
    • 3.1视图
    • 3.2序列化类
  • 四.短信注册接口
    • 4.1视图
    • 4.2序列化类
    • 4.3路由
  • 五.频率限制
  • 六.前台登录注册功能
    • 6.1校验手机号是否注册过了
    • 6.2发送验证码
    • 6.3注册
    • src/components/Register.vue

一.集成了腾讯短信(封装)

luffyapi/lib/t_sms/settings.py

在小luffyapi下建一个lib文件夹,在这个文件夹下再新建一个包t_sms
Python SDK 2.0使用指南

# 短信应用 SDK AppID
APPID = 1400009099  # SDK AppID 以1400开头
# 短信应用 SDK AppKey
APPKEY = "9ff91d87c2cd7cd0ea762f141975d1df37481d48700d70ac37470aefc60f9bad"
# 短信模板ID,需要在短信控制台中申请
TEMPLATE_ID = 7839  # NOTE: 这里的模板 ID`7839` 只是示例,真实的模板 ID 需要在短信控制台中申请
# 签名
SMS_SIGN = "腾讯云"  # NOTE: 签名参数使用的是`签名内容`,而不是`签名ID`。这里的签名"腾讯云"只是示例,真实的签名需要在短信控制台中申请

luffyapi/lib/t_sms/sms.py

from . import settings  # 包内部,使用相对导入
from qcloudsms_py import SmsSingleSender

# 生成验证码函数(4位数字)
import random
from utils.logging import get_logger
logger=get_logger('sms')
def get_code():
    code = ''
    for i in range(4):
        code += str(random.randint(0, 9))
    return code


# 发送验证码的函数


def send_code(mobile_phone:str, code:str, min=3):
    # 放到配置文件中
    appid = settings.APPID
    appkey = settings.APPKEY
    template_id = settings.TEMPLATE_ID
    sms_sign = settings.SMS_SING
    phone_numbers = [mobile_phone, ]
    ssender = SmsSingleSender(appid, appkey)
    params = [code, min]
    try:
        result = ssender.send_with_param(86, phone_numbers[0], template_id, params, sign=sms_sign, extend="", ext="")
        if result and result.get('result') == 0:
            return True,'发送成功'
        else:
            logger.error('短信发送失败,失败原因为:%s' % str(result.get('errmsg')))
            return False,str(result.get('errmsg'))
    except Exception as e:
        logger.error('短信发送失败,失败原因为:%s'%str(e))
        return False,str(e)

二.短信验证码接口

1 前端发送get请求,携带手机号,调用封装好的发送短信接口,完成发送短信,给用户返回提示信息
2 路由:send_code  视图函数:SendCodeView
3 如果有人恶意使用你的接口如何解决
	-1 限制频率:手机号一分钟一次
	-2 限制ip:一分钟一次
	-3 发送验证码之前,先输入验证码(集成了极验滑动验证码)

2.1路由,配置

## luffyapi/lib/t_sms/__init__.py
from .sms import get_code,send_code

# 配置验证码缓存
## luffyapi/settings/user_settings.py
# 验证码缓存时间
SMS_CACHE_TIME=3*60
SMS_CACHE_KEY = 'sms_cache_%(mobile)s'

# 路由
router.register('',views.SendCodeView,basename='sendcodeview')


#luffyapi/ luffyapi / settings / dev.py
## 缓存到内存中
CACHES = {
    'default': {
        'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',  # 指定缓存使用的引擎
        'LOCATION': 'unique-snowflake',  # 写在内存中的变量的唯一值
        'TIMEOUT': 300,  # 缓存超时时间(默认为300秒,None表示永不过期)
        'OPTIONS': {
            'MAX_ENTRIES': 300,  # 最大缓存记录的数量(默认300)
            'CULL_FREQUENCY': 3,  # 缓存到达最大个数之后,剔除缓存个数的比例,即:1/CULL_FREQUENCY(默认3)
        }
    }
}

2.2视图

# 自动生成路由
from rest_framework.viewsets import  ViewSetMixin,ViewSet
from rest_framework.views import APIView

import re
from lib.t_sms import get_code,send_code

from django.core.cache import cache
from django.conf import settings
# class SendCodeView(ViewSetMixin,APIView):
class  SendCodeView(ViewSet):
    @action(methods=['get',],detail=False)
    def send_code(self,request,*args,**kwargs):
        mobile = request.GET.get('mobile')
        # 1验证手机号是否合法
        if re.match(r'^1[3-9][0-9]{9}$',mobile):
            # 2生成验证码
            code=get_code()
            print(code)  # 上线记得删除这行代码
            # 3发送验证码
            res,msg=send_code(mobile,code)
            if res:
                # 4发送成功,验证码保存(保存在哪)
                # 存到缓存中
                # cache.set('',code,60*3)
                # sms_cache_18953666666  缓存时间  3分钟
                cache.set(settings.SMS_CACHE_KEY %{'mobile':mobile},code, settings.SMS_CACHE_TIME)
                return APIResponse(msg=msg)
            else:
                return APIResponse(status=1,msg=msg)
        else:
            return APIResponse(status=1,msg='手机号不合法')
        # 5返回给前端信息

三.短信登录接口

1 手机号+验证码 --->post请求
2 手机号,code:
	-拿着手机号去用户表,比较,如果该手机号存在,是我的注册用户
	-code去缓存中取出来--->手机号在用户表中,并且code是正确的,让它登录成功,签发token
3 更便捷登录:使用本手机一键登录,一键注册
4 路由:code_login --->post请求--->{mobile:111111,code:1234}
	-127.0.0.0:8000/user/code_login/--->post

3.1视图

@action(methods=['post', ], detail=False)
def code_login(self, request, *args, **kwargs):
    ser = serializer.LoginCodeSerialzer(data=request.data, context={'request': request})
    if ser.is_valid():
        token = ser.context['token']
        user = ser.context['user']
        icon = ser.context['icon']
        return APIResponse(token=token, username=user.username, icon=icon, id=user.id)
    else:
        return APIResponse(status=1, msg=ser.errors)

3.2序列化类

import re
from rest_framework import exceptions
from django.core.cache import cache
class LoginCodeSerializer(serializers.ModelSerializer):
    mobile=serializers.CharField()  # 重写该字段
    code=serializers.CharField()

    class Meta:
        model=models.User
        fields=['mobile','code']


    # 局部钩子,验证手机号是否合法
    # 手机号格式校验(手机号是否存在校验规则自己考量)
    def validate_mobile(self,value):
        if not re.match(r'^1[3-9][0-9]{9}$', value):
            raise exceptions.ValidationError('mobile field error')
        return value

    # 全局钩子
    def _check_code(self,attrs):
        mobile=attrs.get('mobile')
        code_in=attrs.get('code')
        code=cache.get(settings.SMS_CACHE_KEY%{'mobile':mobile})
        # 开发测试阶段可能会留的,万能验证码
        if code_in == code or code_in == '1234':
            return mobile
        else:
            raise exceptions.ValidationError('验证码不合法')


    def _get_user(self,mobile):
        user=models.User.objects.filter(mobile=mobile,is_active=True).first()
        if user:
            return user
        else:
            raise exceptions.ValidationError('手机号不存在')

    def _get_token(self,user):
         payload=jwt_payload_handler(user)
         token=jwt_encode_handler(payload)
         return token

    def validate(self,attrs):
         # 通过手机号获取用户
         # 比较验证码是否一致
         # 签发token
         request=self.context.get('request')
         # 验证码校验 - 需要验证码与手机号两者参与
         mobile=self._check_code(attrs)
         # 多方式得到user
         user=self._get_user(mobile)
         # user签发token
         token=self._get_token(user)
         # token用context属性携带给视图类
         self.context['token']=token
         # 将登录用户对象直接传给视图
         self.context['user']=user
         icon='http://%s%s%s'%(request.META['HTTP_HOST'],settings.MEDIA_URL,user.icon)
         self.context['icon']=icon
         return attrs

四.短信注册接口

1 手机号+验证码--->完成注册
2 路由:user/register --->post请求--->{mobile:11111,code:1234,password:111}

4.1视图

# 自动生成路由,跟数据打交道,create方法
from rest_framework.generics import GenericAPIView
from rest_framework.mixins import CreateModelMixin
from rest_framework.viewsets import GenericViewSet

# class CodeRegister(ViewSetMixin,GenericAPIView,CreateModelMixin):
class CodeRegister(GenericViewSet,CreateModelMixin):
    queryset = models.User.objects.all()
    serializer_class = serializer.CodeRegisterSerializer

    def create(self,request,*args,**kwargs):
        res=super().create(request,*args,**kwargs)
        return APIResponse(msg='注册成功',username=res.data.get('username'),mobile=res.data.get('mobile'))

4.2序列化类

class CodeRegisterSerializer(serializers.ModelSerializer):
    code=serializers.CharField(write_only=True)

    class Meta:
        model=models.User
        fields=['username','mobile','code','password']
        extra_kwargs={
            'password':{'write_only':True},
            'username':{'read_only':True}
        }

    def validate(self,attrs):
        # 手机号是否合法,是否存在
        mobile=attrs.get('mobile')
        code_in=attrs.get('code')
        if re.match(r'^1[3-9][0-9]{9}$', mobile):
            # 手机号是唯一字段,还没走到这里就已经报了全局异常,数据库中mobile已经有了,所有不用在这里校验手机号是否存在
            # user = models.User.objects.filter(mobile=mobile).first()
            # if user:
            #     raise exceptions.ValidationError('该用户已经注册了')
            # code是否正确
            code=cache.get(settings.SMS_CACHE_KEY % {'mobile': mobile})
            if code == code_in or '1234' == code_in:  #留后门
                # 新建用户
                attrs.pop('code')  # 验证码不是表中的字段,需要pop出来
                return attrs
            else:
                raise exceptions.ValidationError('验证码不合法')
        else:
            raise exceptions.ValidationError('手机号不合法')
    def create(self,validate_data):
        # 密码不是密文
        mobile = validate_data.get('mobile')
        user=models.User.objects.create_user(username=mobile,**validate_data)
        return user

4.3路由

router.register('register',views.CodeRegister,basename='CodeRegister')

五.频率限制

luffyapi/ luffyapi / settings / dev.py

# restframework的配置
REST_FRAMEWORK = {
    'EXCEPTION_HANDLER': 'utils.exception.exception_handler',
    'DEFAULT_THROTTLE_RATES': {
        'sms': '1/min'
    }
}

luffyapi/ luffyapi / apps / user / throttles.py

from rest_framework.throttling import SimpleRateThrottle
from django.core.cache import cache
from django.conf import settings
# 结合手机验证码接口来书写
class SMSRateThrottle(SimpleRateThrottle):
    scope = 'sms'
    def get_cache_key(self, request, view):
        # 手机号是通过get请求提交的
        mobile = request.query_params.get('mobile', None)
        if not mobile:
            return None  # 不限制

        # 手机验证码发送失败,不限制,只有发送成功才限制,如果需求是发送失败也做频率限制,就注释下方三行
        code = cache.get(settings.SMS_CACHE_KEY % {'mobile': mobile})
        if not code:
            return None

        # return self.cache_format % {
        #     'scope': self.scope,
        #     'ident': mobile,
        # }

        return 'throttle_%s_%s'%(self.scope,mobile)

六.前台登录注册功能

6.1校验手机号是否注册过了

// 校验手机号是否注册过了
      this.$axios.get(this.$settings.base_url + '/user/check_mobile/?mobile=' + this.mobile).then(res => {
        // eslint-disable-next-line eqeqeq
        if (res.data.status == 1) {
          // 可以正常注册
          this.$message({
            message: '手机号可以正常注册',
            type: 'warning',
            duration: 1000
          })
          this.is_send = true
        } else {
          this.$message({
            message: res.data.msg,
            type: 'warning',
            duration: 1000,
            onClose: () => {
              this.mobile = ''
            }
          })
        }
      })


6.2发送验证码

// 发送验证码
      this.$axios.get(this.$settings.base_url + '/user/send_code/?mobile=' + this.mobile).then(res => {
        this.$message({
          message: res.data.msg,
          type: 'info',
          duration: 1000
        })
      })

6.3注册

给注册绑定事件

<el-button type="primary" @click="go_register">注册</el-button>
go_register () {
      this.$axios.post(this.$settings.base_url + '/user/register/', {
        mobile: this.mobile,
        code: this.sms,
        password: this.password
      }).then(res => {
        this.$message({
          message: res.data.msg,
          type: 'info',
          duration: 1000
        })
        // eslint-disable-next-line eqeqeq
        if (res.data.status == 0) {
          // 跳转到登录
          this.go_login()
        }
      })
    }

src/components/Register.vue

<template>
    <div class="register">
        <div class="box">
            <i class="el-icon-close" @click="close_register"></i>
            <div class="content">
                <div class="nav">
                    <span class="active">新用户注册</span>
                </div>
                <el-form>
                    <el-input
                            placeholder="手机号"
                            prefix-icon="el-icon-phone-outline"
                            v-model="mobile"
                            clearable
                            @blur="check_mobile">
                    </el-input>
                    <el-input
                            placeholder="密码"
                            prefix-icon="el-icon-key"
                            v-model="password"
                            clearable
                            show-password>
                    </el-input>
                    <el-input
                            placeholder="验证码"
                            prefix-icon="el-icon-chat-line-round"
                            v-model="sms"
                            clearable>
                        <template slot="append">
                            <span class="sms" @click="send_sms">{{ sms_interval }}</span>
                        </template>
                    </el-input>
                    <el-button type="primary" @click="go_register">注册</el-button>
                </el-form>
                <div class="foot">
                    <span @click="go_login">立即登录</span>
                </div>
            </div>
        </div>
    </div>
</template>

<script>
    export default {
        name: "Register",
        data() {
            return {
                mobile: '',
                password: '',
                sms: '',
                sms_interval: '获取验证码',
                is_send: false,
            }
        },
        methods: {
            close_register() {
                this.$emit('close', false)
            },
            go_login() {
                this.$emit('go')
            },
            check_mobile() {
                if (!this.mobile) return;
                if (!this.mobile.match(/^1[3-9][0-9]{9}$/)) {
                    this.$message({
                        message: '手机号有误',
                        type: 'warning',
                        duration: 1000,
                        onClose: () => {
                            this.mobile = '';
                        }
                    });
                    return false;
                }
                //校验手机号是否注册过了
                this.$axios.get(this.$settings.base_url + '/user/check_mobile/?mobile=' + this.mobile).then(res => {
                    if (res.data.status == 1) {
                        //可以正常注册
                        this.$message({
                            message: '手机号可以正常注册',
                            type: 'warning',
                            duration: 1000,
                        });
                        this.is_send = true;

                    } else {
                        this.$message({
                            message: res.data.msg,
                            type: 'warning',
                            duration: 1000,
                            onClose: () => {
                                this.mobile = '';
                            }
                        });
                    }
                })

            },
            send_sms() {
                if (!this.is_send) return;
                this.is_send = false;
                let sms_interval_time = 60;
                this.sms_interval = "发送中...";
                let timer = setInterval(() => {
                    if (sms_interval_time <= 1) {
                        clearInterval(timer);
                        this.sms_interval = "获取验证码";
                        this.is_send = true; // 重新回复点击发送功能的条件
                    } else {
                        sms_interval_time -= 1;
                        this.sms_interval = `${sms_interval_time}秒后再发`;
                    }
                }, 1000);
                //发送验证码
                this.$axios.get(this.$settings.base_url + '/user/send_code/?mobile=' + this.mobile).then(res => {
                    this.$message({
                        message: res.data.msg,
                        type: 'info',
                        duration: 1000,
                    });


                })
            },
            go_register() {
                this.$axios.post(this.$settings.base_url + '/user/register/', {
                    'mobile': this.mobile,
                    'code': this.sms,
                    'password': this.password
                }).then(res => {
                    this.$message({
                        message: res.data.msg,
                        type: 'info',
                        duration: 1000,
                    });
                    if (res.data.status == 0) {
                        //跳转到登录
                        this.go_login()
                    }


                })

            },

        }
    }
</script>

<style scoped>
    .register {
        width: 100vw;
        height: 100vh;
        position: fixed;
        top: 0;
        left: 0;
        z-index: 10;
        background-color: rgba(0, 0, 0, 0.3);
    }

    .box {
        width: 400px;
        height: 480px;
        background-color: white;
        border-radius: 10px;
        position: relative;
        top: calc(50vh - 240px);
        left: calc(50vw - 200px);
    }

    .el-icon-close {
        position: absolute;
        font-weight: bold;
        font-size: 20px;
        top: 10px;
        right: 10px;
        cursor: pointer;
    }

    .el-icon-close:hover {
        color: darkred;
    }

    .content {
        position: absolute;
        top: 40px;
        width: 280px;
        left: 60px;
    }

    .nav {
        font-size: 20px;
        height: 38px;
        border-bottom: 2px solid darkgrey;
    }

    .nav > span {
        margin-left: 90px;
        color: darkgrey;
        user-select: none;
        cursor: pointer;
        padding-bottom: 10px;
        border-bottom: 2px solid darkgrey;
    }

    .nav > span.active {
        color: black;
        border-bottom: 3px solid black;
        padding-bottom: 9px;
    }

    .el-input, .el-button {
        margin-top: 40px;
    }

    .el-button {
        width: 100%;
        font-size: 18px;
    }

    .foot > span {
        float: right;
        margin-top: 20px;
        color: orange;
        cursor: pointer;
    }

    .sms {
        color: orange;
        cursor: pointer;
        display: inline-block;
        width: 70px;
        text-align: center;
        user-select: none;
    }
</style>
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

luffy-08 的相关文章

随机推荐

  • Excel怎么比较两列文本是否相同?

    转载自品略图书馆 http www pinlue com article 2020 05 2215 1410586873210 html 这个问题很简单 xff0c 属于Excel基础操作技巧 xff0c 我就分享我最喜欢用的三招给你 xf
  • 为什么电脑唯独搜不到自己家wifi?

    转载自品略图书馆 http www pinlue com article 2020 05 2213 2410586244619 html 电脑唯独搜不到自己家wifi xff0c 别人家的都能搜到 xff0c 手机也可以搜到自己家的 xff
  • http://和www.前缀网站有什么具体区别?

    转载自品略图书馆 http www pinlue com article 2019 03 1813 598231572617 html 将http 和www 放一起比较 xff0c 是没有实际意义的 一 http协议二 域名一 http协议
  • 什么是三层交换机、网关、DNS、子网掩码、MAC地址?

    转载自品略图书馆 http www pinlue com article 2020 08 2313 2511146576256 html 一文讲懂什么是三层交换机 网关 DNS 子网掩码 MAC地址 很多朋友多次问到什么是网关 dns 子网
  • C++类对象共享数据的5种实现方法

    转自 xff1a http www pinlue com article 2020 09 2617 0611262487540 html
  • c语言free的用法

    转自 xff1a http www pinlue com article 2020 03 3100 4610073901713 html
  • Spring Boot 修改默认端口号

    修改配置文件 xff0c 加上参数 xff1a server port 61 8014 或者 xff1a server port 8014 启动后可发现tomcat运行在端口8014上了 实现原因可看以下链接 转载 SpringBoot修改
  • php调用类中的方法

    转自 xff1a http www pinlue com article 2020 06 1219 0410725563037 html
  • 人工智能 : 第三篇”脑机接口“

    本文作者Tim Urban xff1a Wait but Why的作者Tim Urban 是埃隆马斯克 xff08 特斯拉 SpaceX创始人 xff09 强烈推荐的科技博主 他写的AI文章是全世界转发量最高的 他的粉丝还包括 xff1a
  • 如何找回一台丢失的Win10电脑?

    今天说说如何找电脑 为什么小微想到了这个问题 还要从一次关于奇葩办公地点的讨论说起 看到大家的回答 xff0c 小微佩服得五体投地 办公经历还可以如此精jing彩xin绝dong伦po 作为结实靠谱的出行伙伴 ThinkPad陪伴大家出现在
  • 不必再狂按空格键了!Word 里文字对齐推荐这4种方法

    我们在用Word写论文 制作简历的时候 xff0c 通常会遇到把word中某些特定文字对齐的情况 那么问题来了 xff0c 你平时都是怎么对齐文字的 xff1f 傻傻的用空格来对齐吗 xff1f 在字符数不等的情况下 xff0c 加空格不仅
  • AMI主板BIOS菜单图文讲解设置!

    电脑硬件 xff0c 包括电脑中所有物理的零件 xff0c 以此来区分它所包括或执行的数据和为硬件提供指令以完成任务的软件 主要包含 机箱 xff0c 主板 xff0c 总线 xff0c 电源 xff0c 硬盘 xff0c 存储控制器 xf
  • luffy-02

    这里写目录标题 一 昨日回顾二 今日内容1 路飞前台配置 1 重构项目目录 2 文件修订 xff1a 目录中非配置文件的多余文件可以移除router的使用 3 前台配置 全局样式 配置文件 axios vue cookies element
  • luffy-03

    这里写目录标题 一 昨日回顾二 今日内容1 跨域问题1 1后端自己处理跨域简单请求非简单请求中间件处理 1 2前端处理跨域App vuevue config js 2 头部组件 尾部组件components Header vuecompon
  • luffy-04

    这里写目录标题 一 昨日回顾二 今日内容1 路飞项目使用xadmin2 首页轮播图接口 轮播图表 视图类 轮播图数量4 通过配置实现 前端对接 后续 接口缓存 3 git的使用3 1git的工作流程 4 git分支git提交代码出现冲突的2
  • luffy-05

    这里写目录标题 一 首页轮播图接口二 今日内容1 过滤文件2 从远端拉项目3 远程仓库3 1链接远程仓库的两种情况 4 冲突出现的原因及解决 一 首页轮播图接口 span class token number 1 span 首页轮播图接口
  • 手把手教你搭建鸿蒙hi3518开发和运行环境

    前言 学习 C 语言 xff0c C 43 43 语言 xff0c 数据结构和算法 xff0c 操作系统 xff0c 网络 xff0c 驱动 xff0c 设计模式等知识 用鸿蒙来强化就太对了 本文教你一步一步搭建鸿蒙的开发和运行环境 xff
  • luffy-06

    这里写目录标题 一 上节回顾二 今日内容1 ssh链接和https链接2 gitlab3 git远程分支合并4 git冲突出现原因及解决5 首页登录注册vue页面Header vueLogin vue 6 登录注册功能接口分析 一 上节回顾
  • luffy-07

    这里写目录标题 一 昨日回顾二 今日内容1 登录注册前端页面1 1Login vue1 2Register vue1 3Header vue 2 多方式登录接口3 手机号是否存在接口3 1路由层user urls py3 2user ser
  • luffy-08

    这里写目录标题 一 集成了腾讯短信 封装 luffyapi lib t sms settings pyluffyapi lib t sms sms py 二 短信验证码接口2 1路由 配置2 2视图 三 短信登录接口3 1视图3 2序列化类