目录
1.前端对于token的认证
上文我们实现了对于前端能够通过token是否存在来判断用户是否登录,传送门: token对于登录状态的判断
对于token,不仅要判断token是否存在,而且要判断token是否有效
所以接下来我们做的事情:就是验证token是否真的有效
验证token是否有效
1.验证token有效需要引入verify_jwt_token
users/urls.py
from rest_framework_jwt.views import obtain_jwt_token, verify_jwt_token from . import views from django.urls import path urlpatterns = [ ...... path(r'verify/', verify_jwt_token), ]
2.在dev.py中可以设置token的过期时间
可以用来测试如果token过期是否还能登录
import datetime JWT_AUTH = { 'JWT_EXPIRATION_DELTA': datetime.timedelta(days=1), }
3.drf测试
POST /users/login 输入用户名和密码,获取到token值
POST /users/verify 输入token值,获取到token值,用户名,id
4.改写check_login函数
改写之前写的check_login函数,由原来的判断token是否存在-->校验token
并且将check_login函数移动至setting.js作为公共函数 ,因为很多组件都需要用到这个功能
setting.js
// 实现思路:获取token值,并将token值POST提交到/users/verify进行验证 export default { Host:"http://www.lyapi.com:8001",// server address check_login(ths){ let token = localStorage.token || sessionStorage.token; console.log('>>>>>',token); ths.$axios.post(`${this.Host}/users/verify/`,{ token:token, }).then((res)=>{ ths.token = token; }).catch((error)=>{ ths.token = false; }) } }
Vheader.vue
// vheader组件执行一下方法 created(){ this.get_nav_data(); this.$settings.check_login(this); },
2.滑动验证码
1.滑动验证码实现的原理
前端的验证码时如何生成的
其实实际上是前端是需要后端来获取滑动验证码的
2.滑动验证码的代码实现
1.腾讯防水墙的appid和secret key放到dev.py配置文件中
dev.py
# 防水墙配置 FSQ = { 'appid':'2080330111', 'app_serect_key':'07v2KHaK2CMY8tkl_aOrbcA**', }
2.web前端接入腾讯防水墙的js文件
index.html
<!-- index.html --> <script src="https://ssl.captcha.qq.com/TCaptcha.js"></script>
3.点击登录按钮,触发登录按钮绑定的LoginHandle事件
// 通过这两行代码就可以实现点击登录按钮,出现滑动验证码图片了 var captcha1 = new TencentCaptcha('2080330111',function(res){}); captcha1.show(); // res:滑动成功或者失败的响应结果 console.log(res) // {appid:xxx,bizState:xxx,randstr:xxx,ret:0,ticket:xxx}
返回结果字段说明如下:
字段名 | 值类型 | 说明 |
---|---|---|
ret | Int | 验证结果,0:验证成功。2:用户主动关闭验证码。 |
ticket | String | 验证成功的票据,当且仅当 ret = 0 时 ticket 有值。 |
appid | String | 场景 ID。 |
bizState | Any | 自定义透传参数。 |
randstr | String | 本次验证的随机串,请求后台接口时需带上。 |
4.思考
当用户点击登录-->出现滑动验证码-->滑动成功-->给后台发请求,将用户名和密码发送到后端-->
用户名和密码发到后端了,是否代表着滑动验证就通过了吗?
并不是,所以验证码的数据也要在后台校验--->ticket值
5.检查验证码票据结果
如何验证滑动验证码是否滑动成功?
上图只是针对校验滑动验证码滑动是否成功
也可以同时校验 用户名 密码 滑动验证码数据
6.前端滑动验证码的代码实现
methods:{ loginHandle(){ var captcha1 = new TencentCaptcha('2080330111', (res) =>{ if (res.ret === 0){ // 滑动成功后才能够发post请求 this.$axios.post(`${this.$settings.Host}/users/login/`,{ username:this.username, password:this.password, // 将ticket和randstr也发送到后台去,让后台去验证滑动是否成功 ticket:res.ticket, randstr:res.randstr, }).then((res)=>{ console.log(res); // 判断是临时登录还是永久登录 if (this.remember){ localStorage.token = res.data.token; localStorage.username = res.data.username; localStorage.id = res.data.id; sessionStorage.removeItem('token'); sessionStorage.removeItem('username'); sessionStorage.removeItem('id'); }else { sessionStorage.token = res.data.token; sessionStorage.username = res.data.username; sessionStorage.id = res.data.id; localStorage.removeItem('token'); localStorage.removeItem('username'); localStorage.removeItem('id'); } }).catch((error)=>{ this.$alert('用户名或者密码错误', '登录失败', { confirmButtonText: '确定', }); }) } }); captcha1.show(); // 显示验证码 } },
现在已经将数据发送给了后台,那么后台怎样进行校验呢?
昨天我们使用的obtain_jwt_token:只能做用户名和密码的验证,无法实现对滑动成功的验证,
所以我们需要改写代码添加字段,让jwt也能够实现对滑动成功的验证
7.重写jwt代码来实现对滑动成功的认证
users/urls.py
# users/urls.py from rest_framework_jwt.views import verify_jwt_token from . import views from django.urls import path urlpatterns = [
# 因为我们要改写jwt了,所以不能继承原来的obtain_jwt_token了 # 我们要自己改写这部分的视图函数来实现对于滑动成功的认证 path(r'login/', views.CustomLoginView.as_view()), path(r'verify/', verify_jwt_token), ]
users/views.py
# users/views.py from django.shortcuts import render from rest_framework_jwt.views import ObtainJSONWebToken from lyapi.apps.users.serializers import CustomeSerializer class CustomLoginView(ObtainJSONWebToken): serializer_class = CustomeSerializer
users/serializers.py
# users/serializers.py from rest_framework_jwt.serializers import JSONWebTokenSerializer from rest_framework import serializers from rest_framework_jwt.compat import get_username_field, PasswordField from django.utils.translation import ugettext as _ from django.contrib.auth import authenticate, get_user_model from rest_framework_jwt.settings import api_settings User = get_user_model() jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER jwt_decode_handler = api_settings.JWT_DECODE_HANDLER jwt_get_username_from_payload = api_settings.JWT_PAYLOAD_GET_USERNAME_HANDLER class CustomeSerializer(JSONWebTokenSerializer): def __init__(self, *args, **kwargs): """ Dynamically add the USERNAME_FIELD to self.fields. """ super(JSONWebTokenSerializer, self).__init__(*args, **kwargs) # 重写jwt自带的序列化器,在原来的基础上添加ticket和randstr self.fields[self.username_field] = serializers.CharField() self.fields['password'] = PasswordField(write_only=True) self.fields['ticket'] = serializers.CharField(write_only=True) self.fields['randstr'] = serializers.CharField(write_only=True) # 全局钩子函数 def validate(self, attrs): credentials = { self.username_field: attrs.get(self.username_field), 'password': attrs.get('password'), 'ticket': attrs.get('ticket'), 'randstr': attrs.get('randstr'), } if all(credentials.values()): user = authenticate(self.context['request'],**credentials) # self.context['request']当前请求的request对象 if user: if not user.is_active: msg = _('User account is disabled.') raise serializers.ValidationError(msg) payload = jwt_payload_handler(user) return { 'token': jwt_encode_handler(payload), 'user': user } else: msg = _('Unable to log in with provided credentials.') raise serializers.ValidationError(msg) else: msg = _('Must include "{username_field}" and "password".') msg = msg.format(username_field=self.username_field) raise serializers.ValidationError(msg)
users/utils.py
# users/utils.py class CustomeModelBackend(ModelBackend): ''' ' 'ticket': attrs.get('ticket'), 'randstr': attrs.get('randstr'), ''' def authenticate(self, request, username=None, password=None, **kwargs): try: user_obj = get_user_obj(username) ticket = kwargs.get('ticket') userip = request.META['REMOTE_ADDR'] randstr = kwargs.get('randstr') params = { # 腾讯防水墙需要的一些参数 "aid": settings.FSQ.get('appid'), "AppSecretKey": settings.FSQ.get('app_serect_key'), "Ticket": ticket, "Randstr": randstr, "UserIP": userip } params = urlencode(params).encode() # 转换成bytes类型 url = settings.FSQ.get('URL') f = urlopen(url, params) # 发送请求,将数据发送出去,并返回滑动是否成功数据 content = f.read() # 获取数据 res = json.loads(content) # json反序列化 print(res) # {'response': '1', 'evil_level': '0', 'err_msg': 'OK'} if res.get('response') != '1': # 如果滑动失败 return None if user_obj: # 如果用户名存在 if user_obj.check_password(password): # 如果密码正确 return user_obj # 返回用户名对象 else: # 如果用户名或密码错误 return None except Exception: logger.error('验证过程代码有误,请联系管理员') return None