Python项目实战 3.2:验证码.短信验证码

目录

一、短信验证码逻辑分析

二、关于短信平台:

三、短信验证码后端逻辑

四、短信验证码前端逻辑


一、短信验证码逻辑分析

要点

1. 保存短信验证码是为注册做准备的。

2. 为避免用户使用图形验证码恶意测试,后端提取了图形验证码后,立即删除图形验证码。

3. Django不具备发送短信的功能,所以我们借助第三方的短信平台来帮助我们发送短信验证码。

二、关于短信平台:

Python对接 阿里云短信平台_mengnf的专栏-CSDN博客

三、短信验证码后端逻辑

3.1 短信验证码接口设计

1. 请求方式

选项方案
请求方法GET
请求地址/sms_codes/(?P<mobile>1[3-9]\d{9})/

2. 请求参数:路径参数和查询字符串

参数名类型是否必传说明
mobilestring手机号
image_codestring图形验证码
uuidstring唯一编号

3. 响应结果:JSON

字段说明
code状态码
errmsg错误信息

3.2 短信验证码接口定义(verifications\views.py

class SMSCodeView(View):
    """短信验证码"""

    def get(self, request, mobile):
        """
        :param mobile: 手机号
        :return: JSON
        """
        # 接受参数
        image_code_client = request.GET.get('image_code')
        uuid = request.GET.get('uuid')

        # 校验参数
        if not all([image_code_client, uuid]):
            return http.HttpResponseForbidden('缺少必传参数')

        # 创建连接到redis的对象
        redis_conn = get_redis_connection('verify_code')

        # 判断用户是否频繁发送短信验证码
        send_flag = redis_conn.get('send_flag_%s' % mobile)
        if send_flag:
            return http.JsonResponse({'code': RETCODE.THROTTLINGERR, 'errmsg': '发送短信过于频繁'})

        # 提取图形验证码
        image_code_server = redis_conn.get('img_%s' % uuid)
        if image_code_server is None:
            return http.JsonResponse({'code': RETCODE.IMAGECODEERR, 'errmsg': '图形验证码已失效'})
        # 删除图形验证码
        redis_conn.delete('img_%s' % uuid)
        # 对比图形验证码
        image_code_server = image_code_server.decode()  # 将bytes转字符串,再比较
        if image_code_client.lower() != image_code_server.lower():  # 转小写,再比较
            return http.JsonResponse({'code': RETCODE.IMAGECODEERR, 'errmsg': '输入图形验证码有误'})

        # 生成短信验证码:随机6位数字,000007
        sms_code = '%06d' % random.randint(0, 999999)
        logger.info(sms_code)  # 手动的输出日志,记录短信验证码

        # # 保存短信验证码
        # redis_conn.setex('sms_%s' % mobile, constants.SMS_CODE_REDIS_EXPIRES, sms_code)
        # # 保存发送短信验证码的标记
        # redis_conn.setex('send_flag_%s' % mobile, constants.SEND_SMS_CODE_INTERVAL, 1)

        # 创建redis管道
        pl = redis_conn.pipeline()
        # 将命令添加到队列中
        # 保存短信验证码
        pl.setex('sms_%s' % mobile, constants.SMS_CODE_REDIS_EXPIRES, sms_code)
        # 保存发送短信验证码的标记
        pl.setex('send_flag_%s' % mobile, constants.SEND_SMS_CODE_INTERVAL, 1)
        # 执行
        pl.execute()

        # 发送短信验证码
        这里调用发送短信的方法如:阿里云api.send(参数,,,)

        # 响应结果
        return http.JsonResponse({'code': RETCODE.OK, 'errmsg': '发送短信成功'})

四、短信验证码前端逻辑

4.1 Vue绑定短信验证码界面

1.register.html

<li>
    <label>短信验证码:</label>
    <input type="text" v-model="sms_code" @blur="check_sms_code" name="sms_code" id="msg_code" class="msg_input">
    <a @click="send_sms_code" class="get_msg_code">[[ sms_code_tip ]]</a>
    <span class="error_tip" v-show="error_sms_code">[[ error_sms_code_message ]]</span>
    {% if sms_code_errmsg %}
    <span class="error_tip">{{ sms_code_errmsg }}</span>
    {% endif %}
</li>

2.register.js

check_sms_code(){
    if(this.sms_code.length != 6){
        this.error_sms_code_message = '请填写短信验证码';
        this.error_sms_code = true;
    } else {
        this.error_sms_code = false;
    }
},

4.2 axios请求短信验证码

1.发送短信验证码事件处理

// 发送短信验证码
send_sms_code() {
	// 避免恶意用户频繁的点击获取短信验证码的标签
	if (this.send_flag == true) { // 先判断是否有人正在上厕所
		return; // 有人正在上厕所,退回去
	}
	this.send_flag = true; // 如果可以进入到厕所,立即关门

	// 校验数据:mobile,image_code
	this.check_mobile();
	this.check_image_code();
	if (this.error_mobile == true || this.error_image_code == true) {
		this.send_flag = false;
		return;
	}

	let url = '/sms_codes/' + this.mobile + '/?image_code=' + this.image_code + '&uuid=' + this.uuid;
	axios.get(url, {
		responseType: 'json'
	})
		.then(response => {
			if (response.data.code == '0') {
				// 展示倒计时60秒效果
				let num = 60;
				let t = setInterval(() => {
					if (num == 1) { // 倒计时即将结束
						clearInterval(t); // 停止回调函数的执行
						this.sms_code_tip = '获取短信验证码'; // 还原sms_code_tip的提示文字
						this.generate_image_code(); // 重新生成图形验证码
						this.send_flag = false;
					} else { // 正在倒计时
						num -= 1; // num = num - 1;
						this.sms_code_tip = num + '秒';
					}
				}, 1000)
			} else {
				if (response.data.code == '4001') { // 图形验证码错误
					this.error_image_code_message = response.data.errmsg;
					this.error_image_code = true;
				} else { // 4002 短信验证码错误
					this.error_sms_code_message = response.data.errmsg;
					this.error_sms_code = true;
				}
				this.send_flag = false;
			}
		})
		.catch(error => {
			console.log(error.response);
			this.send_flag = false;
		})
},




register.js完整代码:

// 我们采用的时ES6的语法
// 创建Vue对象 vm
let vm = new Vue({
    el: '#app', // 通过ID选择器找到绑定的HTML内容
    // 修改Vue读取变量的语法
    delimiters: ['[[', ']]'],
    data: { // 数据对象
        // v-model
        username: '',
        password: '',
        password2: '',
        mobile: '',
        allow: '',
        image_code_url: '',
        uuid: '',
        image_code: '',
        sms_code_tip: '获取短信验证码',
        send_flag: false, // 类比上厕所,send_flag就是锁,false表示门开,true表示门关
        sms_code: '',

        // v-show
        error_name: false,
        error_password: false,
        error_password2: false,
        error_mobile: false,
        error_allow: false,
        error_image_code: false,
        error_sms_code: false,

        // error_message
        error_name_message: '',
        error_mobile_message: '',
        error_image_code_message: '',
        error_sms_code_message: '',
    },
    mounted() { // 页面加载完会被调用的
        // 生成图形验证码
        this.generate_image_code();
    },
    methods: { // 定义和实现事件方法
        // 发送短信验证码
        send_sms_code() {
            // 避免恶意用户频繁的点击获取短信验证码的标签
            if (this.send_flag == true) { // 先判断是否有人正在上厕所
                return; // 有人正在上厕所,退回去
            }
            this.send_flag = true; // 如果可以进入到厕所,立即关门

            // 校验数据:mobile,image_code
            this.check_mobile();
            this.check_image_code();
            if (this.error_mobile == true || this.error_image_code == true) {
                this.send_flag = false;
                return;
            }

            let url = '/sms_codes/' + this.mobile + '/?image_code=' + this.image_code + '&uuid=' + this.uuid;
            axios.get(url, {
                responseType: 'json'
            })
                .then(response => {
                    if (response.data.code == '0') {
                        // 展示倒计时60秒效果
                        let num = 60;
                        let t = setInterval(() => {
                            if (num == 1) { // 倒计时即将结束
                                clearInterval(t); // 停止回调函数的执行
                                this.sms_code_tip = '获取短信验证码'; // 还原sms_code_tip的提示文字
                                this.generate_image_code(); // 重新生成图形验证码
                                this.send_flag = false;
                            } else { // 正在倒计时
                                num -= 1; // num = num - 1;
                                this.sms_code_tip = num + '秒';
                            }
                        }, 1000)
                    } else {
                        if (response.data.code == '4001') { // 图形验证码错误
                            this.error_image_code_message = response.data.errmsg;
                            this.error_image_code = true;
                        } else { // 4002 短信验证码错误
                            this.error_sms_code_message = response.data.errmsg;
                            this.error_sms_code = true;
                        }
                        this.send_flag = false;
                    }
                })
                .catch(error => {
                    console.log(error.response);
                    this.send_flag = false;
                })
        },
        // 生成图形验证码的方法:封装的思想,代码复用
        generate_image_code() {
            this.uuid = generateUUID();
            this.image_code_url = '/image_codes/' + this.uuid + '/';
        },
        // 校验用户名
        check_username() {
            // 用户名是5-20个字符,[a-zA-Z0-9_-]
            // 定义正则
            let re = /^[a-zA-Z0-9_-]{5,20}$/;
            // 使用正则匹配用户名数据
            if (re.test(this.username)) {
                // 匹配成功,不展示错误提示信息
                this.error_name = false;
            } else {
                // 匹配失败,展示错误提示信息
                this.error_name_message = '请输入5-20个字符的用户名';
                this.error_name = true;
            }

            // 判断用户名是否重复注册
            if (this.error_name == false) { // 只有当用户输入的用户名满足条件时才回去判断
                let url = '/usernames/' + this.username + '/count/';
                axios.get(url, {
                    responseType: 'json'
                })
                    .then(response => {
                        if (response.data.count == 1) {
                            // 用户名已存在
                            this.error_name_message = '用户名已存在';
                            this.error_name = true;
                        } else {
                            // 用户名不存在
                            this.error_name = false;
                        }
                    })
                    .catch(error => {
                        console.log(error.response);
                    })
            }
        },
        // 校验密码
        check_password() {
            let re = /^[0-9A-Za-z]{8,20}$/;
            if (re.test(this.password)) {
                this.error_password = false;
            } else {
                this.error_password = true;
            }
        },
        // 校验确认密码
        check_password2() {
            if (this.password != this.password2) {
                this.error_password2 = true;
            } else {
                this.error_password2 = false;
            }
        },
        // 校验手机号
        check_mobile() {
            let re = /^1[3-9]\d{9}$/;
            if (re.test(this.mobile)) {
                this.error_mobile = false;
            } else {
                this.error_mobile_message = '您输入的手机号格式不正确';
                this.error_mobile = true;
            }

            // 判断手机号是否重复注册
            if (this.error_mobile == false) {
                let url = '/mobiles/' + this.mobile + '/count/';
                axios.get(url, {
                    responseType: 'json'
                })
                    .then(response => {
                        if (response.data.count == 1) {
                            this.error_mobile_message = '手机号已存在';
                            this.error_mobile = true;
                        } else {
                            this.error_mobile = false;
                        }
                    })
                    .catch(error => {
                        console.log(error.response);
                    })
            }
        },
        // 校验图形验证码吗
        check_image_code() {
            if (this.image_code.length != 4) {
                this.error_image_code_message = '请输入图形验证码';
                this.error_image_code = true;
            } else {
                this.error_image_code = false;
            }
        },
        // 校验短信验证码
        check_sms_code() {
            if (this.sms_code.length != 6) {
                this.error_sms_code_message = '请填写短信验证码';
                this.error_sms_code = true;
            } else {
                this.error_sms_code = false;
            }
        },
        // 校验是否勾选协议
        check_allow() {
            if (!this.allow) {
                this.error_allow = true;
            } else {
                this.error_allow = false;
            }
        },
        // 监听表单提交事件
        on_submit() {
            this.check_username();
            this.check_password();
            this.check_password2();
            this.check_mobile();
            this.check_sms_code();
            this.check_allow();

            // 在校验之后,注册数据中,只要有错误,就禁用掉表单的提交事件
            if (this.error_name == true || this.error_password == true || this.error_password2 == true || this.error_mobile == true || this.error_sms_code == true || this.error_allow == true) {
                // 禁用掉表单的提交事件
                window.event.returnValue = false;
            }
        },
    }
});

  • 3
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
可以使用以下正则表达式来匹配以$d开头,disk结尾的字符串: ```python import re string = "# snmpwalk -v 2c -c zte_public 190.254.0.3:1611.3.6.1.4.1.3902.6053.19.1.3.2.7\ ...\n$da 8:15 0 477G 0 disk\n\ ... ~ # snmpwalk -v 2c -c zte_public 190.254.0.3:161 1.3.6.1.4.1.3902.6053.19.1.3.2.7\ ...\nSNMPv2-SMI::enterprises.3902.6053.19.1.3.2.7.1.1.7.1.0 = Gauge32: 0\ ...\n$db 8:15 0 477G 0 disk\n\ ...\nSNMPv2-SMI::enterprises.3902.6053.19.1.3.2.7.1.1.14.0.0 = Gauge32: 0\ ...\nSNMPv2-SMI::enterprises.3902.6053.19.1.3.2.7.1.1.14.1.0 = Gauge32: 0\ ...\nSNMPv2-SMI::enterprises.3902.6053.19.1.3.2.7.1.1.15.1.0 = Gauge32: 0\ ...\n$dc 8:15 0 477G 0 disk\n\ ...\nSNMPv2-SMI::enterprises.3902.6053.19.1.3.2.7.1.1.20.1.0 = Gauge32: 0\ ...\nSNMPv2-SMI::enterprises.3902.6053.19.1.3.2.7.1.1.26.0.0 = Gauge32: 0\ ...\n$dd 8:15 0 477G 0 disk\n\ ...\nSNMPv2-SMI::enterprises.3902.6053.19.1.3.2.7.1.1.26.1.0 = Gauge32: 0" pattern = r'\$d.*?disk\\n\\s*\\\.\\.\\..*?' result = re.findall(pattern, string, re.DOTALL) print(result) # 输出结果:['$da\t\t\t\t\t8:15\t0\t477G\t0\tdisk\n\\ ...', '$db\t\t\t\t\t8:15\t0\t477G\t0\tdisk\n\\ ...', '$dc\t\t\t\t\t8:15\t0\t477G\t0\tdisk\n\\ ...', '$dd\t\t\t\t\t8:15\t0\t477G\t0\tdisk\n\\ ...'] ``` 解析一下正则表达式: - `\` :转义字符,表示后面的字符是特殊字符,需要转义处理; - `\$d` :匹配以$d开头的字符串; - `.*?` :非贪婪匹配任意字符,直到下一个字符能够匹配成功; - `disk\\n\\s*` :匹配disk和换行符,并且后面可能有多个空白字符; - `\\\.\\\.\\.` :匹配三个连续的点(因为点是特殊字符,需要转义); - `.*?` :非贪婪匹配任意字符,直到下一个字符能够匹配成功; - `.*?` 和 `\\\.\\\.\\.` 重复出现多次,因为要匹配多个符合条件的字符串; - `re.DOTALL` :表示`.`可以匹配任意字符,包括换行符。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值