vue_drf之实现极验滑动验证码

vue

  一、需求

  1,场景

  我们在很多登录和注册场景里,为了避免某些恶意攻击程序,我们会添加一些验证码,也就是行为验证,让我们相信现在是一个人在交互,而不是一段爬虫程序。现在市面上用的比较多的,比较流行的是极验的滑动验证码" title="滑动验证码">滑动验证码。

  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>&nbsp;&nbsp;&nbsp;&nbsp;

<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>&nbsp;&nbsp;&nbsp;&nbsp;

<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>

View Code

   这是上面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之实现极验滑动验证码 的全部内容, 来源链接: utcz.com/z/380380.html

回到顶部