T O P

[资源分享]     day78:luffy:前端对于token的认证&滑动验证码的实现

  • By - 楼主

  • 2020-10-29 22:40:55
  • 目录

    1.前端对于token的认证

    2.滑动验证码

      1.滑动验证码实现的原理

      2.滑动验证码的代码实现

        1.配置文件

        2.前端实现:Login.vue

        3.后端实现:改写jwt代码

    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

     

    本帖子中包含资源

    您需要 登录 才可以下载,没有帐号?立即注册