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>

 

 

原文地址:https://www.cnblogs.com/usherwang/p/14234992.html