图片验证及短信校验
1. 图片验证码
这种图片验证方式是我们最常见的形式,它可以有效的防范恶意攻击者采用恶意工具,调用“动态验证码短信获取”接口进行动态短信发送, 导致接入用户短信被刷,造成账号余额损失。同时这种动态发送方式会朝许多无关的手机用户,发送很多验证码短信,导致手机用户被骚扰,甚至引起用户投诉。这种恶意攻击究其原因是攻击者可以自动对接口进行大量调用。
如果网站在用户进行“动态验证码短信发送” 操作前,要求用户输入图片验证码,确认用户是真实有效后,服务器端再发送动态短信到用户手机上。这一种流程就可以有效的解决恶意攻击问题。正确的加入图片验证码的方式是在短信验证码发送前,先让用户填写图片验证码,再发送短信验证码。
Uuid, 唯一身份标识码, 在用户注册之前,网站没有任何东西来区分 不同的 客户端, 因此 在客户端 生成 一个 uuid, django中通过不同的uuid 来 区分 不同的客户端
2. 图片验证码生成与展示
django
#1.在项目主目录下新建utils文件夹,下载captcha文件夹,放到utils下
#2.写生成验证码接口
from utils.captcha.captcha import captcha
#生成图片验证码
class ImageCodeAPIView(APIView):
def get(self,request):
name,text,pic = captcha.generate_captcha()
return HttpResponse(pic, content_type='image/jpg')
路由
from django.urls import path, re_path
from user.views import *
urlpatterns = [
path('image_code/', ImageCodeAPIView.as_view()),
]
Django-redis使用
1、安装
pip install django-redis
2、配置redis
# setting.py
CACHES = {
"default": {
"BACKEND": "django_redis.cache.RedisCache",
"LOCATION": "redis://127.0.0.1:6379/1",
"OPTIONS": {
"CLIENT_CLASS": "django_redis.client.DefaultClient",
}
}
}
3、redis连接封装
utils下面新建一个common.py文件
#redis连接
def get_redis_conn():
from django_redis import get_redis_connection
conn = get_redis_connection()
return conn
验证码图片redis存储
from utils import comm
#生成图片验证码
class GetCaptcha(APIView):
def get(self,request):
#获取uuid
uuid = request.GET.get('uuid')
#生成验证码图片
name,text,pic = captcha.generate_captcha()
#获取redis连接
redis_con = comm.get_redis_conn()
#图片验证码存储在redis里面,设置过期时间
redis_con.setex(uuid, 60, text) #以秒为单位
#以图片的方式输出
return HttpResponse(pic, content_type='image/jpg')
vue
- 定义方法
- 生成uuid
- 将 uuid拼接到请求网址中,生成 图片验证码的url
- 将拼接好的图片验证码url绑定到image标签中
- 初始加载页面时,需要 在 钩子函数中挂载方法
- 定义图片的点击事件,触发方法,生成新的uuid,重新请求图片
<!-- 4. 对img标签进行属性绑定 -->
<img :src="image_code_url" alt="图形验证码" class="pic_code">
<!-- 5. 对img标签进行点击事件绑定 ,点击一次,执行一次方法,请求一次图片 ->
<script>
// 使用uuid模块,生成 uuid
import {v4 as uuid4} from 'uuid' // 提前安装好uuid模块, cnpm install uuid --save
export default {
name: "Register",
data() {
return {
image_code_id: '', // 实际就是uuid
image_code_url: '', // 图片验证码的地址
}
},
methods: {
get_img_url(){
this.uid = uuid4()
this.image_url = 'http://localhost:8000/users/getimage/?uuid='+uid
}
},
mounted() {
// 挂载执行方法,让网页初始加载时,就可以正常显示图片验证码
this.get_img_url();
}
}
</script>
3. 图片验证码认证
django
- 接收前端发送的参数:
image_code_id
和 用户输入的验证码 - 取出redis中的 验证码
- 如果取不到,说明过期,返回对应的错误信息即可
- 如果取到验证码,进行对比,注意: 统一大小写
4.短信验证
容联云
- 官方地址:https://www.yuntongxun.com/
- 注册帐号
- 阅读文档:https://doc.yuntongxun.com/p/5a531a353b8496dd00dcdfe2
- 创建短信验证码应用:https://www.yuntongxun.com/member/app/toAppAdd
由于容联云也是采用Restful接口进行功能实现,那么发送短信需要模拟请求接口,需要使用python中的requests模块
pip install requests
SDK发送
- 安装sdk
pip install ronglian_sms_sdk
- 官方接口调用方式
from ronglian_sms_sdk import SmsSDK
accId = '8a216da878005a800178a251439d39da'
accToken = 'dc4fd2e2a23e45a68474dbc6d1233eda'
appId = '8a216da878005a800178a251449839e1'
import json
def send_message(mobile,code,time):
sdk = SmsSDK(accId, accToken, appId)
tid = '1'
datas = (code, time)
resp = sdk.sendMessage(tid, mobile, datas)
result = json.loads(resp)
if result['statusCode'] == '000000':
return 1
return 2
发送短信验证码,验证图片验证码
#发送短信验证码
import random
class SendMes(APIView):
def get(self,request):
#接收客户端发送的数据
imagecode = request.GET.get('imagecode')
mobile = request.GET.get('mobile')
uuid = request.GET.get('uuid')
if not all([imagecode,mobile]):
return Response({'code':10017})
#验证图片验证码
conn = get_redis_conn()
#redis中取验证码
code = conn.get(uuid)
if code:
code = str(code,encoding='utf8')
#图片验证码对比
if imagecode.lower() == code.lower():
#验证通过后调用发送短信接口
sms_code = random.randint(10000,99999)
result = send_message(mobile,sms_code,1)
#假如短信验证码发送成功
if result:
#redis存短信验证码
conn.setex(mobile,60,sms_code)
#把图片验证码从redis删除
conn.delete(uuid)
return Response({'code':200})
else:
return Response({'code':10020})
else:
return Response({'code':10019})
else:
return Response({'code':10018})
vue页面
//获取短信验证码
getMsgCode(){
//获取用户输入的图片验证码
var imagecode = this.picCode
var mobile = this.form.mobile
this.axios.get('users/sendMes/?imagecode='+imagecode+'&mobile='+mobile+'&uuid='+this.imageuuid).then(res=>{
console.log(res)
})
}
5. 用户注册
校验密码
Vue
- 用户输入密码之后,触发失焦事件
- 在方法中,使用正则校验密码
- 用户输入 确认密码之后,触发失焦事件
- 在方法中,确认密码和密码必须一致
<li>
<label>密码:</label>
<input type="password" name="pwd" id="pwd" v-model="pwd" @blur="check_pwd">
<span class="error_tip" v-show="pwd_flag">请输入8-20位的密码</span>
</li>
<li>
<label>确认密码:</label>
<input type="password" name="cpwd" id="cpwd" v-model="pwd2" @blur="check_pwd2">
<span class="error_tip" v-show="pwd2_flag">两次输入的密码不一致</span>
</li>
<script>
export default {
name: "Register",
data() {
return {
pwd: '', // 密码
pwd_flag: false,
pwd2: '', //确认密码
pwd2_flag: false
}
},
methods: {
// 校验密码
check_pwd() {
let reg = /^.{8,20}$/
if (reg.test(this.pwd) === false) {
this.pwd_flag = true
} else {
this.pwd_flag = false
}
},
// 校验确认密码
check_pwd2() {
if (this.pwd === this.pwd2) {
this.pwd2_flag = false
} else {
this.pwd2_flag = true
}
}
}
}
</script>
用户注册
django
from rest_framework.views import APIView
from rest_framework.response import Response
from user.models import *
class RegisterAPIView(APIView):
def post(self, request):
# 1. 获取参数
username = request.data.get('username')
pwd = request.data.get('pwd')
pwd2 = request.data.get('pwd2')
mobile = request.data.get('mobile')
flag = request.data.get('flag')
# 2. 校验参数
if not all([username, pwd, pwd2, mobile, flag]):
return Response({'msg': '缺少必要参数'}, status=400)
if pwd != pwd2:
return Response({'msg': '密码不一致'}, status=400)
# if flag == 'false':
# return Response({'msg': '必须同意注册协议'}, status=400)
# 3. 保存入库
try:
User.objects.create_user(username=username, password=pwd, mobile=mobile)
except Exception as e:
return Response({'msg': '注册失败'}, status=500)
# 4. 返回结果
return Response({'msg': '注册成功'}, status=201)
Vue页面功能实现
<li class="reg_sub">
<input type="submit" value="注 册" @click.prevent="register">
</li>
<script>
export default {
name: "Register",
...
methods: {
// 注册方法
register() {
// 校验用户名、密码、确认密码、手机号、图片验证码、是否同意
this.check_username();
this.check_pwd();
this.check_pwd2();
this.check_mobile();
this.check_image_code();
this.check_flag();
// 任何一个参数有问题,都不能注册
if (this.username_flag || this.pwd_flag || this.pwd2_flag || this.mobile_flag || this.image_code_flag || this.flag_flag) {
return
}
// 构建表单参数
let userinfo = new FormData();
userinfo.append('username', this.username);
userinfo.append('pwd', this.pwd);
userinfo.append('pwd2', this.pwd2);
userinfo.append('mobile', this.mobile);
userinfo.append('flag', this.flag);
this.$axios.post('register/', userinfo)
.then(resp => {
console.log(resp.data)
})
.catch(err => {
console.log(err.response.data)
})
}
}
}
</script>