vue_drf之实现极验滑动验证码
一、需求
1,场景
我们在很多登录和注册场景里,为了避免某些恶意攻击程序,我们会添加一些验证码,也就是行为验证,让我们相信现在是一个人在交互,而不是一段爬虫程序。现在市面上用的比较多的,比较流行的是极验的滑动验证码。
2,伪代码
1,当打开登录页面时,页面还没加载完毕,浏览器就自动往服务器发送一个get请求,主要是请求极验滑动验证码的相关数据,页面接收到相关数据后,在页面渲染出一个滑动验证码组件,
2,用户输入用户名和密码后,点击滑动验证码,进行验证,验证成功后会自动往服务器发送一个post请求,服务器会产生一个随机数,保存在redis中,然后也把这个随机数返回
3,当验证码验证成功后,用户点击登录按钮,这次会发送post请求,携带用户名,密码,接收到的随机数,服务器对接收到的数据进行校验,当验证成功后,给前端返回一个token,token中包含用户的信息,然后前端再跳转到其他页面上
二、代码
login.vue
<template>
<div id="login">
<div class="box">
<p>
<img src="../../assets/login_title.png" alt="">
</p>
<p class="sign">帮助有志向的年轻人通过努力学习获得体面的工作和生活!</p>
<div class="pass" v-show="num==1">
<div class="title2 cursor">
<span @click="num=1" :class="num==1 ? 'show' :''">密码登录</span>
<span @click="num=2" :class="num==2 ? 'show' :''">短信登录</span>
</div>
<input v-model="username" type="text" class="ss" placeholder="用户名 / 手机号码">
<input v-model="password" type="password" class="ss" placeholder="密码">
<div id="captcha" class="ss"></div>
<div class="t1">
<div class="left">
<input type="checkbox" class="cursor" v-model="remenber">
<div class="remenber cursor" >记住密码</div>
</div>
<div class="right cursor">忘记密码</div>
</div>
<button class="login_btn" @click="login1">登录</button>
<div class="register">
没有账号
<span><router-link to="/register">立即注册</router-link></span>
</div>
</div>
<div class="messge" v-show="num==2">
<div class="title2 cursor">
<span @click="num=1" :class="num==1 ? 'show' :''">密码登录</span>
<span @click="num=2" :class="num==2 ? 'show' :''">短信登录</span>
</div>
<input v-model="phone" type="text" class="ss" placeholder="手机号码">
<div class="sms">
<input v-model="sms_code" type="text" class="ss">
<div class="content" @click="get_sms_code">{{content}}</div>
</div>
<button class="login_btn" @click="sms_login">登录</button>
<div class="register">
没有账号
<span><router-link to="/register">立即注册</router-link></span>
</div>
</div>
</div>
</div>
</template> <script>
export default {
name:'login',
data:function () {
return {
num:1,
username:'',
password:'',
remenber:'',
status:'',
content:'获取验证码',
phone:'',
sms_code:'',
verify_code:'',
}
},
methods:{
//手机号和短信验证码登录
sms_login:function(){
let _this=this;
this.$axios.post('http://127.0.0.1:8000/user/login/',{
'username':_this.phone,
'password':_this.sms_code,
'type':'phone',
},{responseType:'json'})
.then(function (res) {
sessionStorage.token=res.data.token;
_this.$router.push('/home');
}).catch(function (error) {
console.log(error.response)
});
},
//获取短信验证码
get_sms_code:function(){
let reg = /1[3-9]{2}\d{8}/;
if( reg.test(this.phone) ){
if(this.content == "获取验证码"){
let _this=this;
this.$axios.get('http://127.0.0.1:8000/user/sms?type=login&phone='+this.phone)
.then(function (res) {
if(res.data.message==0){
_this.content=60;
let this_=_this;
let tt=setInterval(function () {
if (this_.content>=1){
this_.content--
}
else {
this_.content='获取验证码';
clearInterval(tt)
}
},1000);
alert('验证码发送成功');
}
}).catch(function (error) {
console.log(error.response)
})
}
}else {
alert('手机号码有误')
}
},
//用户名和密码登录
login1:function () {
if (this.status==1){
let _this=this;
this.$axios.post('http://127.0.0.1:8000/user/login/',{
'username':_this.username,
'password':_this.password,
'verify_code':_this.verify_code,
'type':'username'
},{responseType:'json'})
.then(function (res) {
if (res.status==200){
if (_this.remenber){
sessionStorage.removeItem('token');
localStorage.token=res.data.token;
}
else {
localStorage.removeItem('token');
sessionStorage.token=res.data.token
}
_this.$router.push('/home');
}
else {
alert('用户名或密码错误')
}
})
.catch(function (error) {
console.log(error.response)
// alert(error.response.data.non_field_errors[0]);
});
}
else {
alert('验证码错误')
}
},
//二次校验滑动验证码
handlerPopup:function (captchaObj) {
let _this=this;
captchaObj.onSuccess(function () {
var validate = captchaObj.getValidate();
_this.$axios.post("http://127.0.0.1:8000/user/yzm/",{
geetest_challenge: validate.geetest_challenge,
geetest_validate: validate.geetest_validate,
geetest_seccode: validate.geetest_seccode,
},{
responseType:"json",
}).then(function (res) {
_this.verify_code=res.data.verify_code;
_this.status=res.data.status
}).catch(function (error) {
console.log(error)
})
});
captchaObj.appendTo("#captcha");
}
},
//请求滑动验证码
created:function () {
let _this=this;
this.$axios.get("http://127.0.0.1:8000/user/yzm")
.then(function (res) {
let data=JSON.parse(res.data);
initGeetest({
width:'350px',
gt: data.gt,
challenge: data.challenge,
product: "popup",
offline: !data.success
}, _this.handlerPopup);
}).catch(function (error) {
console.log(error)
})
} }
</script> <style scoped>
#login{
background: url('../../assets/Login.jpg');
background-size: 100% 100%;
height: 100%;
position: fixed;
width: 100%;
}
.box{
width: 500px;
height: 600px;
margin: 0 auto;
margin-top: 200px;
text-align: center;
}
.box img{
width: 190px;
height: auto;
}
.box p{
margin: 0;
}
.sign{
font-size: 18px;
color: #fff;
letter-spacing: .29px;
padding-top: 10px;
padding-bottom: 50px;
}
.pass{
width: 400px;
height: 460px;
margin: 0 auto;
background-color: white;
border-radius: 4px;
}
.messge{
width: 400px;
height: 390px;
margin: 0 auto;
background-color: white;
border-radius: 4px;
}
.title2{
width: 350px;
font-size: 20px;
color: #9b9b9b;
padding-top: 50px;
border-bottom: 1px solid #e6e6e6;
margin: 0 auto;
margin-bottom: 20px;
}
.ss{
width: 350px;
height: 45px;
border-radius: 4px;
border: 1px solid #d9d9d9;
text-indent: 20px;
font-size: 14px;
margin-bottom: 20px;
}
.pass .t1{
width: 350px;
margin: 0 auto;
height: 20px;
line-height: 20px;
font-size: 12px;
text-align: center;
position: relative;
}
.t1 .right{
position: absolute;
right: 0;
}
.remenber{
display: inline-block;
position: absolute;
left: 20px;
}
.left input{
position: absolute;
left:0;
width: 14px;
height: 14px;
}
.login_btn{
width: 350px;
height: 45px;
background: #ffc210;
border-radius: 5px;
font-size: 16px;
color: #fff;
letter-spacing: .26px;
margin-top: 30px;
outline: none;
border:none;
cursor: pointer;
}
.register{
margin-top: 20px;
font-size: 14px;
color: #9b9b9b;
}
.register span{
color: #ffc210;
cursor: pointer;
}
.cursor{
cursor: pointer;
}
.show{
display: inline-block;
padding-bottom: 5px;
border-bottom: 2px solid orange;
color: #4a4a4a;
}
a{
text-decoration: none;
color: #ffc210;
}
#captcha{
margin: 0 auto;
height: 44px;
}
.sms{
position: relative;
width: 350px;
height: 45px;
margin: 0 auto;
line-height: 45px;
}
.sms .content{
position: absolute;
top:0;
right: 10px;
color: orange;
border-left: 1px solid orange;
padding-left: 10px;
cursor: pointer; }
</style>
这是上面login.vue代码中的逻辑处理部分
export default {
name:'login',
data:function () {
return {
num:1,
username:'',
password:'',
remenber:'',
status:'',
content:'获取验证码',
phone:'',
sms_code:'',
verify_code:'',
}
},
methods:{//用户名和密码登录
login1:function () {
if (this.status==1){
let _this=this;
this.$axios.post('http://127.0.0.1:8000/user/login/',{
'username':_this.username,
'password':_this.password,
'verify_code':_this.verify_code,
'type':'username'
},{responseType:'json'})
.then(function (res) {
if (res.status==200){
if (_this.remenber){
sessionStorage.removeItem('token');
localStorage.token=res.data.token;
}
else {
localStorage.removeItem('token');
sessionStorage.token=res.data.token
}
_this.$router.push('/home');
}
else {
alert('用户名或密码错误')
}
})
.catch(function (error) {
console.log(error.response)
// alert(error.response.data.non_field_errors[0]);
});
}
else {
alert('验证码错误')
}
},
//二次校验滑动验证码
handlerPopup:function (captchaObj) {
let _this=this;
captchaObj.onSuccess(function () {
var validate = captchaObj.getValidate();
_this.$axios.post("http://127.0.0.1:8000/user/yzm/",{
geetest_challenge: validate.geetest_challenge,
geetest_validate: validate.geetest_validate,
geetest_seccode: validate.geetest_seccode,
},{
responseType:"json",
}).then(function (res) {
_this.verify_code=res.data.verify_code;
_this.status=res.data.status
}).catch(function (error) {
console.log(error)
})
});
captchaObj.appendTo("#captcha");
}
},
//请求滑动验证码
created:function () {
let _this=this;
this.$axios.get("http://127.0.0.1:8000/user/yzm")
.then(function (res) {
let data=JSON.parse(res.data);
initGeetest({
width:'350px',
gt: data.gt,
challenge: data.challenge,
product: "popup",
offline: !data.success
}, _this.handlerPopup);
}).catch(function (error) {
console.log(error)
})
}
}
geetest.py 后端滑动验证码的依赖文件
#!coding:utf8
import sys
import random
import json
import requests
import time
from hashlib import md5 if sys.version_info >= (3,):
xrange = range VERSION = "3.0.0" class GeetestLib(object):
"""极验验证码的核心类"""
FN_CHALLENGE = "geetest_challenge"
FN_VALIDATE = "geetest_validate"
FN_SECCODE = "geetest_seccode" GT_STATUS_SESSION_KEY = "gt_server_status" API_URL = "http://api.geetest.com"
REGISTER_HANDLER = "/register.php"
VALIDATE_HANDLER = "/validate.php"
JSON_FORMAT = False def __init__(self, captcha_id, private_key):
self.private_key = private_key
self.captcha_id = captcha_id
self.sdk_version = VERSION
self._response_str = "" def pre_process(self, user_id=None,new_captcha=1,JSON_FORMAT=1,client_type="web",ip_address=""):
"""
验证初始化预处理.
//TO DO arrage the parameter
"""
status, challenge = self._register(user_id,new_captcha,JSON_FORMAT,client_type,ip_address)
self._response_str = self._make_response_format(status, challenge,new_captcha)
return status def _register(self, user_id=None,new_captcha=1,JSON_FORMAT=1,client_type="web",ip_address=""):
pri_responce = self._register_challenge(user_id,new_captcha,JSON_FORMAT,client_type,ip_address)
if pri_responce:
if JSON_FORMAT == 1:
response_dic = json.loads(pri_responce)
challenge = response_dic["challenge"]
else:
challenge = pri_responce
else:
challenge=" "
if len(challenge) == 32:
challenge = self._md5_encode("".join([challenge, self.private_key]))
return 1,challenge
else:
return 0, self._make_fail_challenge() def get_response_str(self):
return self._response_str def _make_fail_challenge(self):
rnd1 = random.randint(0, 99)
rnd2 = random.randint(0, 99)
md5_str1 = self._md5_encode(str(rnd1))
md5_str2 = self._md5_encode(str(rnd2))
challenge = md5_str1 + md5_str2[0:2]
return challenge def _make_response_format(self, success=1, challenge=None,new_captcha=1):
if not challenge:
challenge = self._make_fail_challenge()
if new_captcha:
string_format = json.dumps(
{'success': success, 'gt':self.captcha_id, 'challenge': challenge,"new_captcha":True})
else:
string_format = json.dumps(
{'success': success, 'gt':self.captcha_id, 'challenge': challenge,"new_captcha":False})
return string_format def _register_challenge(self, user_id=None,new_captcha=1,JSON_FORMAT=1,client_type="web",ip_address=""):
if user_id:
register_url = "{api_url}{handler}?gt={captcha_ID}&user_id={user_id}&json_format={JSON_FORMAT}&client_type={client_type}&ip_address={ip_address}".format(
api_url=self.API_URL, handler=self.REGISTER_HANDLER, captcha_ID=self.captcha_id, user_id=user_id,new_captcha=new_captcha,JSON_FORMAT=JSON_FORMAT,client_type=client_type,ip_address=ip_address)
else:
register_url = "{api_url}{handler}?gt={captcha_ID}&json_format={JSON_FORMAT}&client_type={client_type}&ip_address={ip_address}".format(
api_url=self.API_URL, handler=self.REGISTER_HANDLER, captcha_ID=self.captcha_id,new_captcha=new_captcha,JSON_FORMAT=JSON_FORMAT,client_type=client_type,ip_address=ip_address)
try:
response = requests.get(register_url, timeout=2)
if response.status_code == requests.codes.ok:
res_string = response.text
else:
res_string = ""
except:
res_string = ""
return res_string def success_validate(self, challenge, validate, seccode, user_id=None,gt=None,data='',userinfo='',JSON_FORMAT=1):
"""
正常模式的二次验证方式.向geetest server 请求验证结果.
"""
if not self._check_para(challenge, validate, seccode):
return 0
if not self._check_result(challenge, validate):
return 0
validate_url = "{api_url}{handler}".format(
api_url=self.API_URL, handler=self.VALIDATE_HANDLER)
query = {
"seccode": seccode,
"sdk": ''.join( ["python_",self.sdk_version]),
"user_id": user_id,
"data":data,
"timestamp":time.time(),
"challenge":challenge,
"userinfo":userinfo,
"captchaid":gt,
"json_format":JSON_FORMAT
}
backinfo = self._post_values(validate_url, query)
if JSON_FORMAT == 1:
backinfo = json.loads(backinfo)
backinfo = backinfo["seccode"]
if backinfo == self._md5_encode(seccode):
return 1
else:
return 0 def _post_values(self, apiserver, data):
response = requests.post(apiserver, data)
return response.text def _check_result(self, origin, validate):
encodeStr = self._md5_encode(self.private_key + "geetest" + origin)
if validate == encodeStr:
return True
else:
return False def failback_validate(self, challenge, validate, seccode):
"""
failback模式的二次验证方式.在本地对轨迹进行简单的判断返回验证结果.
"""
if not self._check_para(challenge, validate, seccode):
return 0
validate_result = self._failback_check_result(
challenge, validate,)
return validate_result def _failback_check_result(self,challenge,validate):
encodeStr = self._md5_encode(challenge)
if validate == encodeStr:
return True
else:
return False def _check_para(self, challenge, validate, seccode):
return (bool(challenge.strip()) and bool(validate.strip()) and bool(seccode.strip())) def _md5_encode(self, values):
if type(values) == str:
values = values.encode()
m = md5(values)
return m.hexdigest()
views.py #处理滑动验证码
#滑动验证码
class VerifyCode(APIView):
status=''
user_id=''
"""验证码类"""
def get(self,request):
"""获取验证码"""
user_id = random.randint(1, 100)
APP_ID = "xxxxxxxxxxx" #这两个数据都是在极验官网你的个人界面有的
APP_KEY = "xxxxxxxxxxx"
gt = GeetestLib(APP_ID,APP_KEY)
status = gt.pre_process(user_id,JSON_FORMAT=0, ip_address="127.0.0.1")
VerifyCode.status=status
# request.session[gt.GT_STATUS_SESSION_KEY] = status
# request.session["user_id"] = user_id
VerifyCode.user_id=user_id
data = gt.get_response_str()
return Response(data) def post(self,request):
"""校验验证码"""
APP_ID = "xxxxxxxxxxxxx"
APP_KEY = "xxxxxxxxx"
gt = GeetestLib(APP_ID, APP_KEY)
# status = request.session[gt.GT_STATUS_SESSION_KEY]
status=VerifyCode.status
challenge = request.data.get(gt.FN_CHALLENGE,'')
validate = request.data.get(gt.FN_VALIDATE,'')
seccode = request.data.get(gt.FN_SECCODE,'')
# user_id = request.session["user_id"]
user_id=VerifyCode.user_id
if status:
result = gt.success_validate(challenge, validate, seccode, user_id)
else:
result = gt.failback_validate(challenge, validate, seccode)
VerifyCode.status=''
VerifyCode.user_id=''
if result:
verify_code = "%06d" % random.randint(0, 99999999)
redis = get_redis_connection("verify_code")
redis.setex(verify_code,60, 1)
result={"status":1,'verify_code':verify_code}
else:
result = {"status": 0}
return Response(result)
utils.py 登录验证用户信息
from django.contrib.auth.backends import ModelBackend
from django_redis import get_redis_connection def jwt_response_payload_handler(token, user=None, request=None):
"""
自定义jwt认证成功返回数据
:token 返回的jwt
:user 当前登录的用户信息[对象]
:request 当前本次客户端提交过来的数据
"""
return {
'token': token,
'id': user.id,
'username': user.username,
} #实现多功能登录
import re
from .models import User
def get_user_by_account(account):
"""
根据帐号获取user对象
:param account: 账号,可以是用户名,也可以是手机号
:return: User对象 或者 None
"""
try:
if re.match('^1[3-9]\d{9}$', account):
# 帐号为手机号
user = User.objects.get(phone=account)
else:
# 帐号为用户名
user = User.objects.get(username=account)
except User.DoesNotExist:
return None
else:
return user def sms_code_verify(phone,sms_code):
redis = get_redis_connection("sms_code")
value=redis.get('sms_%s'%phone).decode()
if value==sms_code:
return True
return False
#这是对滑动验证码的随机数进行校验
def verify_code(req):
verify_code = req.data.get('verify_code')
redis = get_redis_connection('verify_code')
value = redis.get(verify_code)
return None or value
class UsernameMobileAuthBackend(ModelBackend):
"""
自定义用户名或手机号认证
"""
def authenticate(self, request, username=None, password=None, **kwargs):
ty=request.data.get('type')
user = get_user_by_account(username)
if ty=='phone' and user is not None and sms_code_verify(username,password):
return user
elif user is not None and user.check_password(password) and verify_code(request):
return user
else:
return None
vue_drf之实现极验滑动验证码的更多相关文章
- thinkphp整合系列之极验滑动验证码
对于建站的筒子们来说:垃圾广告真是让人深恶痛绝:为了清净:搞个难以识别的验证码吧:又被用户各种吐槽:直到后来出现了极验这个滑动的验证码:这真是一个体验好安全高的方案:官网:http://www.gee ...
- selenium+java破解极验滑动验证码的示例代码
转自: https://www.jianshu.com/p/1466f1ba3275 selenium+java破解极验滑动验证码 卧颜沉默 关注 2017.08.15 20:07* 字数 3085 ...
- Python——破解极验滑动验证码
极验滑动验证码 以上图片是最典型的要属于极验滑动认证了,极验官网:http://www.geetest.com/. 现在极验验证码已经更新到了 3.0 版本,截至 2017 年 7 月全球已有十六万家 ...
- python验证码识别(2)极验滑动验证码识别
目录 一:极验滑动验证码简介 二:极验滑动验证码识别思路 三:极验验证码识别 一:极验滑动验证码简介 近些年来出现了一些新型验证码,不想旧的验证码对人类不友好,但是这种验证码对于代码来说识别难度上 ...
- Python 破解极验滑动验证码
Python 破解极验滑动验证码 测试开发社区 1周前 阅读目录 极验滑动验证码 实现 位移移动需要的基础知识 对比两张图片,找出缺口 获得图片 按照位移移动 详细代码 回到顶部 极验滑动验证码 以 ...
- selenium处理极验滑动验证码
要爬取一个网站遇到了极验的验证码,这周都在想着怎么破解这个,网上搜了好多知乎上看到有人问了这问题https://www.zhihu.com/question/28833985,我按照这思路去大概实现了 ...
- selenium+java破解极验滑动验证码
摘要 分析验证码素材图片混淆原理,并采用selenium模拟人拖动滑块过程,进而破解验证码. 人工验证的过程 打开威锋网注册页面(https://passport.feng.com/?r=user/r ...
- luffy之多条件登录与极验滑动验证码
多条件登录 JWT扩展的登录视图,在收到用户名与密码时,也是调用Django的认证系统中提供的authenticate()来检查用户名与密码是否正确. 我们可以通过修改Django认证系统的认证后端( ...
- thinkphp整合系列之极验滑动验证码geetest
给一个央企做官网,登录模块用的thinkphp验证码类.但是2019-6-10到12号,国家要求央企检验官网漏洞,防止黑客攻击,正直贸易战激烈升级时期,所以各事业单位很重视官网安全性,于是乎集团总部就 ...
随机推荐
- linux系统中使用socket直接发送ARP数据
这个重点是如这样创建socket: sock_send = socket ( PF_PACKET , SOCK_PACKET , htons ( ETH_P_ARP) ) ; 其后所有收发的数据都是 ...
- VS中Debug与Release、_WIN32与_WIN64的区别
一.Debug与Release 1. 区别 Debug——调试版,生成的.exe中包含很多调试信息,若直接发包,比较大: Release——发布版 2. 如何区分是Debug编译还是Release ...
- webstorm验证码
2017-2-16 亲测可用 http://idea.imsxm.com/ webstorm10 注册码 User or company Name: EMBRACE ===== LICENSE KEY ...
- Office Web addin 踩坑计:替换后台网站为MVC框架时遇到的问题
Office Web Addin 模板程序的后台本质上是一个网站,你在调试的时候可以发现它的进程是一个32位的IE进程 所以可以把它替换成Asp.net的网站. 替换方法: 1.点击WordRevie ...
- Runtime "Apache Tomcat v6.0 (3)" is invalid. The JRE could not be found. Edit the server and change the JRE location解决方案
使用eclipse,启动Tomcat时出现The JRE could not be found ,Edit server and change teh JRE location的错误提示! 原因:重装 ...
- Windows下安装BeautifulSoup4显示'You are trying to run the Python 2 version of Beautiful Soup under Python 3.(`python setup.py install`) or by running 2to3 (`2to3 -w bs4`).'
按照网上教程,将cmd的目录定位到解压缩文件夹地址,然后 >>python setup.py install ( Window下不能直接解压tar.giz文件,可以使用7z解压软件提取解压 ...
- ES6中的箭头函数和普通函数有什么区别?
1.普通函数中的this总是指向调用它的那个对象, 箭头函数没有自己的this,他的this永远指向其定义环境,任何方法都改变不了其指向,如call().bind().apply().(正是因为它没有 ...
- unity API 之EventSystem.current.IsPointerOverGameObject()
命名空间 :UnityEngine.EventSystems 官方描述: public bool IsPointerOverGameObject(); public bool IsPointerOve ...
- BeanPostProcessor —— 连接Spring IOC和AOP的桥梁
之前都是从大Boss的视角,来介绍Spring,比如IOC.AOP. 今天换个视角,从一个小喽啰出发,来加深对Spring的理解. 这个小喽啰就是, BeanPostProcessor (下面简称 B ...
- 一个applicationContext 加载错误导致的阻塞解决小结
问题为对接一个sso的验证模块,正确的对接姿势为,接入一个 filter, 然后接入一个 SsoListener . 然而在接入之后,却导致了应用无法正常启动,或者说看起来很奇怪,来看下都遇到什么样的 ...