zoukankan      html  css  js  c++  java
  • 0108 luffy登录注册接口

    昨日回顾

    1、git的协同操作
    	1)拿公钥换源远程仓库源链接 - 成为项目开发者
    	2)根据源链接克隆远程仓库 - git clone 源地址
    	3)参与项目的团队开发,永远要遵循 先pull后push,在pull之前要将所有修改的代码提交到本地版本库
    
    2、冲突解决
    	1)当不同开发者协同开发,导致远程仓库与本地仓库的版本不一致,在pull远程仓库到本地仓库时,会出现版本冲突
    	2)两个仓库不同版本中,出现了相同文件的修改情况,会出现文件的冲突
    	3)同一文件的修改,代码有重叠,一定会产生代码冲突,打开冲突的文件,文件中会表示冲突的开始与结束,分割线上下分别是冲突的代码
    	>>>>>>>>>>header
    	===========
    	<<<<<<<<<<1321adsa21
    	4)冲突的解决没有固定的结果,但是要将冲突的标识删除,根据代码实际情况,线下沟通,整合代码即可
    	
    3、登录业务
    	1)多方式登录
    	2)短信验证码
    		腾讯短信服务 - 创建短信服务应用(appid、appkey),申请签名与模板
    			-- 安装对应sdk
    			-- 通过短信服务应用得到短信发送者sender
    			-- 结合签名与模板、手机、验证码、其他所需参数,发送验证码
    	3)手机验证码登录
    	
    	
    4、注册业务
    	1)手机注册验证
    	2)短信验证码
    	3)手机验证码密码注册
    

    luffy后台

    验证手机号是否已注册

    // url.py
    
    urlpatterns = [
        path('mobile/',views.MobileCheckAPIView.as_view()),
    ]
        
    ----------------------------------------------------------
    
    // views.py
    
    # 手机验证码是否已注册
    class MobileCheckAPIView(APIView):
        def get(self,request,*args,**kwargs):
            # 从拼接参数中获取手机号
            mobile = request.query_paramas.get('mobile')
            # 对获取的数据进行校验
            if not mobile:
                return APIResponse(1,'mobile必须提供',http_status=400)
            # 对手机号格式进行校验
            if not re.match(r'1[3-9][0-9]{9}$',mobile):
                return APIResponse(1,msg='mobile格式有误',http_status=400)
            try:
                # 只要数据库中有,就代表已注册
                models.User.objects.get(mobile=mobile)
                return APIResponse(2,msg='手机号已注册')
            except:
                return APIResponse(0,msg='手机号未注册')
            
    ---------------------------------------------------------
    

    使用手机号与验证码注册

    '''urls.py'''
    
        # 手机号验证码方式的注册接口
      path('register/mobile/',views.RegisterMobileAPIView.as_view()),
    -------------------------------------------------------
    
    '''views.py'''
    
    # 手机号与验证码注册
    class RegisterMobileAPIView(APIView):
        def post(self,request):
            # 将前端传的数据进行反序列化处理(保存数据库)
            serializer = serializers.RegisterMobileserializer(data=request.data)
            serializer.is_valid(raise_exception=True)
            # 校验通过保存获取obj对象
            user_obj = serializer.save
            # 将user对象序列化输出得到data,当做返回数据
            return APIResponse(results=serializers.RegisterMobileserializer(user_obj).data)
        
    ----------------------------------------------------------
    
    '''serializers.py'''
    
    # 手机号验证码注册的序列化与反序列化
    class RegisterMobileserializer(serializers.ModelSerializer):
        # 反序列化的字段code(保存数据库)
        code = serializers.CharField(write_only=True,min_length=6,max_length=6)
        class Meta:
            model = models.User
            fields = ('username', 'mobile','password','code')
            # username,mobile序列化与反序列化,password不进行序列化给前端
            extra_kwargs = {
                'password': {
                    'write_only': True
                },
                'username': {
                    'read_only': True
                }
            }
    
        # 每一个反序列化字段都可以配置一个局部钩子
        #     注册提交后对手机号进行校验,对验证码格式进行校验,校验验证码一致
        # 手机号校验钩子
        def validate_mobile(self,value):
            if not re.match(r'^1[3-9][0-9]{9}$', value.username):  # 电话
                raise serializers.ValidationError('手机号格式不正确')
            return value
    
        # 验证码格式内容有误就不需要进行 取服务器存储的验证码(IO操作) 进行校验
        def validate_code(self, value):
            try:
                # 验证码格式数字
                int(value)
                return value
            except:
                raise serializers.ValidationError('验证码格式有误')
    
    
        # 全局校验
    
        def validate(self, attrs):
            mobile = attrs.get('mobile')
            code = attrs.pop('code')
            # 封装的获取云短信的函数方法
            old_code = cache.get(settings.SMS_CACHE_FORMAT % mobile)
            if code != old_code:
                raise serializers.ValidationError({'code': '验证码有误'})
            # 创建用户需要一些额外的信息,比如username
            attrs['username'] = mobile
            return attrs
    
        #create方法是否需要重写: 默认入库的密码是明文(重写)
        def create(self, validated_data):
            # auth组件的create_user方法进行密码密文创建
            return models.User.objects.create_user(**validated_data)
        
    

    luffy前台

    避免页面横向产生滚动条

    会随着屏幕的缩放而缩放'overflow: hidden;'
    
    /*避免横向产生滚动条*/
    body {
        overflow: hidden;
    }
    

    img

    header.vue

    <template>
    
        <div class="header">
            <div class="slogan">
                <p>老男孩IT教育 | 帮助有志向的年轻人通过努力学习获得体面的工作和生活</p>
            </div>
            <div class="nav">
                <ul class="left-part">
                    <li class="logo">
                        <router-link to="/">
                            <img src="../assets/img/head-logo.svg" alt="">
                        </router-link>
                    </li>
                    <li class="ele">
                        <span @click="goPage('/free-course')" :class="{active: url_path === '/free-course'}">免费课</span>
                    </li>
                    <li class="ele">
                        <span @click="goPage('/course')" :class="{active: url_path === '/course'}">实战课</span>
                    </li>
                    <li class="ele">
                        <span @click="goPage('/light-course')" :class="{active: url_path === '/light-course'}">轻课</span>
                    </li>
                </ul>
    
    
                <div class="right-part">
                    <div v-if="!token">
                        <span @click="pull_login">登录</span>
                        <span class="line">|</span>
                        <span @click="pull_register">注册</span>
                    </div>
                    <div v-else>
                        <span>{{ username }}</span>
                        <span class="line">|</span>
                        <span @click="logout">注销</span>
                    </div>
                </div>
    
            </div>
    
    
            <Login v-if="is_login" @close="close_login" @go="pull_register" @success="login_success"/>
            <Register v-if="is_register" @close="close_register" @go="pull_login" @success="register_success"/>
    
        </div>
    
    </template>
    
    <script>
        import Login from './Login'
        import Register from './Register'
    
        export default {
            name: "Header",
            components: {
                Login,
                Register,
            },
            data() {
                return {
                    url_path: sessionStorage.url_path || '/',
                    is_login: false,
                    is_register: false,
                    token: $cookies.get('token') || '',
                    username: $cookies.get('username') || '',
                }
            },
            methods: {
                goPage(url_path) {
                    // 已经是当前路由就没有必要重新跳转
                    if (this.url_path !== url_path) {
                        this.$router.push(url_path);
                    }
                    sessionStorage.url_path = url_path;
                },
                // 显示登录模态框
                pull_login() {
                    this.is_login = true;
                    this.close_register();
                },
                close_login() {
                    this.is_login = false;
                },
                pull_register() {
                    this.is_register = true;
                    this.close_login();
                },
                close_register() {
                    this.is_register = false;
                },
                login_success() {
                    this.close_login();
                    this.token = this.$cookies.get('token') || '';
                    this.username = this.$cookies.get('username') || '';
                },
                register_success() {
                    this.pull_login();
                },
                logout() {
                    this.$cookies.remove('token');
                    this.$cookies.remove('username');
                    this.token = '';
                    this.username = '';
                }
            },
            created() {
                sessionStorage.url_path = this.$route.path;
                this.url_path = this.$route.path;
                // 也可以在data中只做空声明,钩子中初始化
                // this.token = this.$cookies.get('token') || '';
                // this.username = this.$cookies.get('username') || '';
            }
        }
    </script>
    
    <style scoped>
        .header {
            background-color: white;
            box-shadow: 0 0 5px 0 #aaa;
        }
    
        .header:after {
            content: "";
            display: block;
            clear: both;
        }
    
        .slogan {
            background-color: #eee;
            height: 40px;
        }
    
        .slogan p {
             1200px;
            margin: 0 auto;
            color: #aaa;
            font-size: 13px;
            line-height: 40px;
        }
    
        .nav {
            background-color: white;
            user-select: none;
             1200px;
            margin: 0 auto;
    
        }
    
        .nav ul {
            padding: 15px 0;
            float: left;
        }
    
        .nav ul:after {
            clear: both;
            content: '';
            display: block;
        }
    
        .nav ul li {
            float: left;
        }
    
        .logo {
            margin-right: 20px;
        }
    
        .ele {
            margin: 0 20px;
        }
    
        .ele span {
            display: block;
            font: 15px/36px '微软雅黑';
            border-bottom: 2px solid transparent;
            cursor: pointer;
        }
    
        .ele span:hover {
            border-bottom-color: orange;
        }
    
        .ele span.active {
            color: orange;
            border-bottom-color: orange;
        }
    
        .right-part {
            float: right;
        }
    
        .right-part .line {
            margin: 0 10px;
        }
    
        .right-part span {
            line-height: 68px;
            cursor: pointer;
        }
    
        .search {
            float: right;
            position: relative;
            margin-top: 22px;
        }
    
        .search input, .search button {
            border: none;
            outline: none;
            background-color: white;
        }
    
        .search input {
            border-bottom: 1px solid black;
        }
    
        .search input:focus {
            border-bottom-color: orange;
        }
    
        .search input:focus + button {
            color: orange;
        }
    
        .search .tips {
            position: absolute;
            bottom: 3px;
            left: 0;
        }
    
        .search .tips span {
            border-radius: 11px;
            background-color: #eee;
            line-height: 22px;
            display: inline-block;
            padding: 0 3px;
            margin-right: 3px;
            cursor: pointer;
            color: #666;
            font-size: 14px;
        }
    </style>
    

    登录模态框

    img

    img

    img

    img

    img

    img

    img

    img

    img

    img

    <template>
        <div class="login">
            <div class="box">
                <i class="el-icon-close" @click="close_login"></i>
                <div class="content">
                    <div class="nav">
                        <span :class="{active: login_method === 'is_pwd'}"
                              @click="change_login_method('is_pwd')">密码登录</span>
                        <span :class="{active: login_method === 'is_sms'}"
                              @click="change_login_method('is_sms')">短信登录</span>
                    </div>
                    <el-form v-if="login_method === 'is_pwd'">
                        <el-input
                                placeholder="用户名/手机号/邮箱"
                                prefix-icon="el-icon-user"
                                v-model="username"
                                clearable>
                        </el-input>
                        <el-input
                                placeholder="密码"
                                prefix-icon="el-icon-key"
                                v-model="password"
                                clearable
                                show-password>
                        </el-input>
                        <el-button type="primary" @click="login">登录</el-button>
                    </el-form>
                    <el-form v-if="login_method === 'is_sms'">
                        <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-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="login_mobile">登录</el-button>
                    </el-form>
                    <div class="foot">
                        <span @click="go_register">立即注册</span>
                    </div>
                </div>
            </div>
        </div>
    </template>
    
    <script>
        export default {
            name: "Login",
            data() {
                return {
                    username: '',
                    password: '',
                    mobile: '',
                    sms: '',
                    login_method: 'is_pwd',
                    sms_interval: '获取验证码',
                    is_send: false,
                }
            },
            methods: {
                close_login() {
                    this.$emit('close')
                },
                go_register() {
                    this.$emit('go')
                },
                change_login_method(method) {
                    this.login_method = method;
                },
                // 校验手机对应用户是否存在
                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({
                        url: this.$settings.base_url + '/user/mobile/',
                        method: 'get',
                        params: {
                            mobile: this.mobile
                        }
                    }).then(response => {
                        if (response.data.status === 0) {
                            this.$message({
                                message: response.data.msg,
                                type: 'warning',
                                duration: 1000,
                            })
                        } else {
                            // 注册过的手机才允许发送验证码
                            this.is_send = true;
                        }
                    }).catch(error => {
                        this.$message({
                            message: error.response.data.msg,
                            type: 'error'
                        })
                    });
                },
                // 发送验证码
                send_sms() {
                    if (!this.is_send) return;
                    this.is_send = false;
                    this.sms_interval = "发送中...";
    
                    // 倒计时
                    let sms_interval_time = 60;
                    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({
                        url: this.$settings.base_url + '/user/sms/',
                        method: 'post',
                        data: {
                            mobile: this.mobile
                        }
                    }).then(response => {
                        if (response.data.status === 0) {
                            // 成功
                            this.$message({
                                message: '验证码发送成功',
                                type: 'success',
                            })
                        } else {
                            // 失败
                            this.$message({
                                message: '验证码发送失败',
                                type: 'error',
                            })
                        }
                    }).catch(() => {
                        // 异常
                        this.$message({
                            message: '获取验证码异常',
                            type: 'error',
                        })
                    });
                },
                // 验证码登录
                login_mobile() {
                    if (!this.mobile || !this.sms) return false;
    
                    this.$axios({
                        url: this.$settings.base_url + '/user/login/mobile/',
                        method: 'post',
                        data: {
                            mobile: this.mobile,
                            code: this.sms,
                        }
                    }).then(response => {
                        // 要将响应的用户信息和token存储到cookies中
                        this.$cookies.set('token', response.data.results.token, '1d');
                        this.$cookies.set('username', response.data.results.username, '1d');
    
                        // 弹出框提示后,关闭登录界面
                        this.$message({
                            message: '登录成功',
                            type: 'success',
                            duration: 1500,
                            onClose: () => {
                                this.$emit('success')
                            }
                        });
                    }).catch(() => {
                        // 异常
                        this.$message({
                            message: '登录失败',
                            type: 'error',
                            duration: 1500,
                            onClose: () => {
                                this.mobile = '';
                                this.sms = '';
                            }
                        })
                    });
                },
                // 密码登录
                login() {
                    if (!this.username || !this.password) return false;
                    this.$axios({
                        url: this.$settings.base_url + '/user/login/',
                        method: 'post',
                        data: {
                            username: this.username,
                            password: this.password,
                        }
                    }).then(response => {
                        // 要将响应的用户信息和token存储到cookies中
                        this.$cookies.set('token', response.data.results.token, '1d');
                        this.$cookies.set('username', response.data.results.username, '1d');
    
                        // 弹出框提示后,关闭登录界面
                        this.$message({
                            message: '登录成功',
                            type: 'success',
                            duration: 1500,
                            onClose: () => {
                                this.$emit('success')
                            }
                        });
                    }).catch(() => {
                        // 异常
                        this.$message({
                            message: '登录失败',
                            type: 'error',
                            duration: 1500,
                            onClose: () => {
                                this.username = '';
                                this.password = '';
                            }
                        })
                    });
                },
            }
        }
    </script>
    
    <style scoped>
        .login {
             100vw;
            height: 100vh;
            position: fixed;
            top: 0;
            left: 0;
            z-index: 10;
            background-color: rgba(0, 0, 0, 0.3);
        }
    
        .box {
             400px;
            height: 420px;
            background-color: white;
            border-radius: 10px;
            position: relative;
            top: calc(50vh - 210px);
            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: 0 20px 0 35px;
            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>
    

    注册模态框

    img

    img

    img

    img

    <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="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({
                        url: this.$settings.base_url + '/user/mobile/',
                        method: 'get',
                        params: {
                            mobile: this.mobile
                        }
                    }).then(response => {
                        if (response.data.status === 2) {
                            this.$message({
                                message: response.data.msg,
                                type: 'warning',
                                duration: 1000,
                            })
                        } else {
                            // 未注册过的手机才允许发送验证码
                            this.is_send = true;
                        }
                    }).catch(error => {
                        this.$message({
                            message: error.response.data.msg,
                            type: 'error'
                        })
                    });
                },
                // 发送验证码
                send_sms() {
                    if (!this.is_send) return;
                    this.is_send = false;
                    this.sms_interval = "发送中...";
    
                    // 倒计时
                    let sms_interval_time = 60;
                    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({
                        url: this.$settings.base_url + '/user/sms/',
                        method: 'post',
                        data: {
                            mobile: this.mobile
                        }
                    }).then(response => {
                        if (response.data.status === 0) {
                            // 成功
                            this.$message({
                                message: '验证码发送成功',
                                type: 'success',
                            })
                        } else {
                            // 失败
                            this.$message({
                                message: '验证码发送失败',
                                type: 'error',
                            })
                        }
                    }).catch(() => {
                        // 异常
                        this.$message({
                            message: '获取验证码异常',
                            type: 'error',
                        })
                    });
                },
                // 注册
                register() {
                    if (!this.mobile || !this.password || !this.sms) return false;
    
                    this.$axios({
                        url: this.$settings.base_url + '/user/register/mobile/',
                        method: 'post',
                        data: {
                            mobile: this.mobile,
                            code: this.sms,
                            password: this.password,
                        }
                    }).then(response => {
                        // 弹出框提示后,关闭注册界面,前台登录页面
                        this.$message({
                            message: '注册成功',
                            type: 'success',
                            duration: 1500,
                            onClose: () => {
                                this.$emit('success')
                            }
                        });
                    }).catch(() => {
                        // 异常
                        this.$message({
                            message: '注册失败',
                            type: 'error',
                            duration: 1500,
                            onClose: () => {
                                this.mobile = '';
                                this.password = '';
                                this.sms = '';
                            }
                        })
                    });
                }
            }
        }
    </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>
    

    用户模块三大认证处理

    img

    # dev.py中设置三大认证
    
    # drf框架的配置
    REST_FRAMEWORK = {
        # 异常模块
        'EXCEPTION_HANDLER': 'utils.exception.exception_handler',
        # 三大认证模块
        'DEFAULT_AUTHENTICATION_CLASSES': [
            'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
        ],
        # 拥有具体权限限制的视图类局部配置权限
        'DEFAULT_PERMISSION_CLASSES': [
            'rest_framework.permissions.AllowAny',
        ],
        # 拥有具体频率限制的视图类局部配置频率
        'DEFAULT_THROTTLE_CLASSES': [],
        # 频率限制scope的规则
        'DEFAULT_THROTTLE_RATES': {
            'sms': '1/min'
        },
    }
    
    ---------------------------------------------------------
    
    # 新建重写的throttles文件
    
    from rest_framework.throttling import SimpleRateThrottle
    
    class SMSRateThrottle(SimpleRateThrottle):
        scope = 'sms'
        def get_cache_key(self, request, view):
            mobile = request.query_params.get('mobile') or request.data.get('mobile')
            if not mobile:
                return None  # 没有提供手机号不进行限制
            return self.cache_format % {
                'scope': self.scope,
                'ident': mobile
            }
    
    # 对views中的发送验证码接口进行频率限制
    
    

    前后台交互

    img

    img

    img

    img

    img

    img

    登录

    前台登录手机号校验

    <script>
        ......
    // 校验手机对应用户是否存在
                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({
                        url: this.$settings.base_url + '/user/mobile/',
                        method: 'get',
                        params: {
                            mobile: this.mobile
                        }
                        // 后台返回数据
                    }).then(response => {
                        if (response.data.status === 0) {
                            // 弹出框,当后台校验的是0,时
                            this.$message({
                                message: response.data.msg,
                                type: 'warning',
                                duration: 1000,  // 弹出框停留时间
                            })
                        } else {
                            // 注册过的手机才允许发送验证码
                            this.is_send = true;
                        }
                        // 前台校验码发送失败
                    }).catch(error => {
                        this.$message({
                            message: error.response.data.msg,
                            type: 'error'
                        })
                    });
                },
    </script>
    

    前台获取验证码

    <script>            
                // 发送验证码
                send_sms() {
                    if (!this.is_send) return;
                    this.is_send = false;
                    this.sms_interval = "发送中...";
    
                    // 倒计时
                    let sms_interval_time = 60;
                    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({
                        url: this.$settings.base_url + '/user/sms/',
                        method: 'post',
                        data: {
                            mobile: this.mobile
                        }
                        // 获取返回值
                    }).then(response => {
                        if (response.data.status === 0) {
                            // 成功
                            this.$message({
                                message: '验证码发送成功',
                                type: 'success',
                            })
                        } else {
                            // 失败
                            this.$message({
                                message: '验证码发送失败',
                                type: 'error',
                            })
                        }
                    }).catch(() => {
                        // 异常
                        this.$message({
                            message: '获取验证码异常',
                            type: 'error',
                        })
                    });
                },
    
    </script>
    

    前台短信验证码登录

    cookies的使用

    在组件逻辑中使用
    
    this.$cookies.set(key, value, exp)  // exp: '1s' | '1h' | '1d' 默认过期时间1天
    	
    this.$cookies.get(key)
    
    this.$cookies.remove(key)
    

    后台设置token的jwt设置

    # dev.py 
    
    # drf-jwt配置
    # 设置token的过期时间
    import datetime
    
    JWT_AUTH = {
        'JWT_EXPIRATION_DELTA': datetime.timedelta(days=1),
        'JWT_ALLOW_REFRESH': False,
        'JWT_REFRESH_EXPIRATION_DELTA': datetime.timedelta(days=7),
    }
    
    

    前台代码

    header中设置组件头,根据token显示登录注册或者用户名注销头部(v-if)
    
    <script>
        // 验证码登录
                login_mobile() {
                    // 判断手机号与验证码是否为空
                    if (!this.mobile || !this.sms) return false;
                    // 后台数据
                    this.$axios({
                        url: this.$settings.base_url + '/user/login/mobile/',
                        method: 'post',
                        data: {
                            mobile: this.mobile,
                            code: this.sms,
                        }
                    }).then(response => {
                        // 要将响应的用户信息和token存储到cookies中
                        this.$cookies.set('token', response.data.results.token, '1d');
                        this.$cookies.set('username', response.data.results.username, '1d');
    
                        // 弹出框提示后,关闭登录界面
                        this.$message({
                            message: '登录成功',
                            type: 'success',
                            duration: 1500,
                            onClose: () => {
                                // 向header组件发送事件,登录成功跳转刷新
                                this.$emit('success')
                            }
                        });
                    }).catch(() => {
                        // 异常
                        this.$message({
                            message: '登录失败',
                            type: 'error',
                            duration: 1500,
                            // 错误,将输入框置为空
                            onClose: () => {
                                this.mobile = '';
                                this.sms = '';
                            }
                        })
                    });
                },
    </script>
    

    前台登录注销

    前台的逻辑(删除前台cookies中的token与username即可)
    

    前台密码登录

    <script>
    
                // 密码登录
                login() {
                    if (!this.username || !this.password) return false;
                    this.$axios({
                        url: this.$settings.base_url + '/user/login/',
                        method: 'post',
                        data: {
                            username: this.username,
                            password: this.password,
                        }
                    }).then(response => {
                        // 要将响应的用户信息和token存储到cookies中
                        this.$cookies.set('token', response.data.results.token, '1d');
                        this.$cookies.set('username', response.data.results.username, '1d');
    
                        // 弹出框提示后,关闭登录界面
                        this.$message({
                            message: '登录成功',
                            type: 'success',
                            duration: 1500,
                            onClose: () => {
                                this.$emit('success')
                            }
                        });
                    }).catch(() => {
                        // 异常
                        this.$message({
                            message: '登录失败',
                            type: 'error',
                            duration: 1500,
                            onClose: () => {
                                this.username = '';
                                this.password = '';
                            }
                        })
    </script>
    

    注册

    <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="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({
                        url: this.$settings.base_url + '/user/mobile/',
                        method: 'get',
                        params: {
                            mobile: this.mobile
                        }
                    }).then(response => {
                        if (response.data.status === 2) {
                            this.$message({
                                message: response.data.msg,
                                type: 'warning',
                                duration: 1000,
                            })
                        } else {
                            // 未注册过的手机才允许发送验证码
                            this.is_send = true;
                        }
                    }).catch(error => {
                        this.$message({
                            message: error.response.data.msg,
                            type: 'error'
                        })
                    });
                },
                // 发送验证码
                send_sms() {
                    if (!this.is_send) return;
                    this.is_send = false;
                    this.sms_interval = "发送中...";
    
                    // 倒计时
                    let sms_interval_time = 60;
                    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({
                        url: this.$settings.base_url + '/user/sms/',
                        method: 'post',
                        data: {
                            mobile: this.mobile
                        }
                    }).then(response => {
                        if (response.data.status === 0) {
                            // 成功
                            this.$message({
                                message: '验证码发送成功',
                                type: 'success',
                            })
                        } else {
                            // 失败
                            this.$message({
                                message: '验证码发送失败',
                                type: 'error',
                            })
                        }
                    }).catch(() => {
                        // 异常
                        this.$message({
                            message: '获取验证码异常',
                            type: 'error',
                        })
                    });
                },
                // 注册
                register() {
                    if (!this.mobile || !this.password || !this.sms) return false;
    
                    this.$axios({
                        url: this.$settings.base_url + '/user/register/mobile/',
                        method: 'post',
                        data: {
                            mobile: this.mobile,
                            code: this.sms,
                            password: this.password,
                        }
                    }).then(response => {
                        // 弹出框提示后,关闭注册界面,前台登录页面
                        this.$message({
                            message: '注册成功',
                            type: 'success',
                            duration: 1500,
                            onClose: () => {
                                this.$emit('success')
                            }
                        });
                    }).catch(() => {
                        // 异常
                        this.$message({
                            message: '注册失败',
                            type: 'error',
                            duration: 1500,
                            onClose: () => {
                                this.mobile = '';
                                this.password = '';
                                this.sms = '';
                            }
                        })
                    });
                }
            }
        }
    </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>
    

    验证码的一次性处理

    后台序列化中全局钩子,校验严验证码
    # 验证码校验通过,验证码失效(验证码一次性使用)
    cache.set(settings.SMS_CACHE_FORMAT % mobile, None, 0)
    
  • 相关阅读:
    Python学习(18)——函数
    Python学习(17)——推导式
    labview隐藏任务栏
    labview下载地址
    labview出现系统998错误
    油猴使用指南
    无法执行该VI,必须使用LabVIEW完整版开发系统才可以解决该错误
    修改远程桌面连接默认端口
    labview使用了报表模块,在生成exe时需要添加以下内容,否则打包后不能开启excel功能
    腾讯通二次开发接口
  • 原文地址:https://www.cnblogs.com/fwzzz/p/12184500.html
Copyright © 2011-2022 走看看