zoukankan      html  css  js  c++  java
  • vue框架前后端分离项目之短信验证码、登录、注册接口及前端登陆注册功能等相关内容-122

    1 短信验证码接口

    1 前端发送get请求,携带手机号,调用封装好的发送短信接口,完成发送短信,给用户返回提示信息
    2 路由:send_code  视图函数:SendCodeView

    3 如果有人恶意使用你的接口如何解决?
    -1 限制频率:手机号一分钟一次
       -2 限制ip:一分钟一次
       -3 发送验证码之前,先输入验证码(集成了极验滑动验证码)

    1.1 路由

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

    1.2 视图

    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()
               # 3发送验证码
               res, msg = send_code(mobile, code)
               if res:
                   # 4发送成功,验证码保存
                   return APIResponse(msg=msg)
               else:
                   return APIResponse(status=1, msg=msg)
           else:
               return APIResponse(status=1, msg='手机号不合法')
           # 5返回给前端信息

    2 短信登录接口

    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

    2.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)

    2.2 序列化类

    class LoginCodeSerialzer(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

    3 短信注册接口

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

    3.1 视图

    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'))

    3.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):
               # 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, validated_data):
           # 密码不是密文
           mobile = validated_data.get('mobile')
           user = models.User.objects.create_user(username=mobile, **validated_data)
           return user

    3.3 路由

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

     

    4 前台登录注册功能

    <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 {
            100vw;
           height: 100vh;
           position: fixed;
           top: 0;
           left: 0;
           z-index: 10;
           background-color: rgba(0, 0, 0, 0.3);
      }

       .box {
            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;
            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 {
            100%;
           font-size: 18px;
      }

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

       .sms {
           color: orange;
           cursor: pointer;
           display: inline-block;
            70px;
           text-align: center;
           user-select: none;
      }
    </style>

     

     

  • 相关阅读:
    The Triangle_DP
    LITTLE SHOP OF FLOWERS_DP
    K Best(最大化平均数)_二分搜索
    Number Game_状态压缩
    Stockbroker Grapevine_Floyd
    A very hard Aoshu problem
    AOE 网络
    AOV网
    最小生成树
    [POJ] 1562 Oil Deposits (DFS)
  • 原文地址:https://www.cnblogs.com/usherwang/p/14234992.html
Copyright © 2011-2022 走看看