vue token过期 当前页数据丢失_Vue结合DjangoRestFramework实现登录认证(下)

ce6a02544fe23f3a1a8986d189ea6588.png

前言

在上一篇 Vue结合Django-Rest-Framework实现登录认证(上)一文中,我们利用token实现了一个非常简单的用户登录认证功能。

那这一篇文章来完成上一篇文章结尾处梳理的需要完善的三个功能点。

1.axios优化
2.用户注销
3.设置登录过期时间

axios优化

axios的优化就是对axios进行一个封装,单独抽离出来一个模块,负责发送getpost请求,同时在请求发送的同时实现认证信息的设置。

// 代码位置:/src/utils/request.js
/*
 * @Description: 封装axios请求 axios官网:http://www.axios-js.com/zh-cn/
 * @version: 1.0.0
 * @Author: houjiaojiao
 * @Date: 2020-07-23 16:32:19
 * @LastEditors: houjiaojiao
 * @LastEditTime: 2020-09-01 17:30:46
 */ 
import axios from 'axios'

// 新建一个 axios 实例
let instance = axios.create({
  baseURL: '/api/cert/',
});
// 请求拦截器
instance.interceptors.request.use(
  // 在发送请求前做一些事情
  request => {
    // 在发送请求前给每个请求头带上Authorization字段
    const auth = 'Token ' + localStorage.getItem('token');
    request.headers.Authorization = auth;
    return request;
  },
  // 请求出现错误做一些事情
  error => {
    console.log('There are some problems with this request');
    console.log(error);
    return Promise.reject(error);
  }
)

//响应拦截器
instance.interceptors.response.use(
  response => {
    return response;
  },
  error => {
    return Promise.reject(error);
  }
)

// 封装get请求
export function get(url, params){
  return new Promise((resolve, reject) => {
      instance.get(url, {
          params
        })
        .then(response => {
          resolve(response);
        }).catch(error => {
          reject(error)
        })
    
  })
}

// 封装post请求
export function post(url, params){
  return new Promise((resolve, reject) => {
    instance.post(url, params)
      .then(response => {
        resolve(response)
      }).catch(error => {
        reject(error)
      })
  })
}

可以看到,我们对axiosgetpost请求进行了封装,同时我们将认证需要添加到请求头部Authorization字段定义在了axios请求拦截器中,这样每一个请求都会携带这个头部字段

接着我们把每一个请求封装成一个API,以登录为例。

// 代码位置:/src/api/login.js
import {get, post} from '@/utils/request.js'

export const login = (loginForm) => post('userAuth/login', loginForm)

然后我们在登录组件中调用这个API发起请求。

// 登录组件 login.vue
// 引入前面封装好的API接口
import {login} from '@/api/login.js'

export default {
    name: 'Login',
    data() {
        return {
            loginForm: {
                username: '',
                password: '',
            }
        }
    },
    methods: {
        login: function(){
            // 直接调用API接口
            login(this.loginForm).then(res => {
                const {result, detail, errorInfo}  = res.data;
                if(result == true){
                    // 登录成功 设置token
                    localStorage.setItem('token', detail.token);
                    // 跳转页面
                    this.$router.push('/certMake');
                }else{
                    this.$message({
                        showClose: true,
                        message: errorInfo,
                        type: 'error'
                    });
                }
           })
        }
    }
}

以上省略登录组件template中的代码。

完成这些以后我们在登录界面输入用户名密码,就可以正常登陆了,这里就不做演示了。之后我们在浏览器中点击其他页面,会发现每个发出的请求头部都携带了Authorization字段。

9622d035a388fc537b5785382387f915.png
3afb51cadde07d4c3dc34299b869563c.png

用户注销

当用户需要注销时,我们应该做的就是清除本地保存的token

logout: function(){
  // 清除token
  localStorage.removeItem("token");
  // 跳转至登录页  登录页面在router.js中的配置的path就是‘/’
  this.$router.push("/");
}

清除以后呢,如果我们直接在浏览器中手动输入url进入某个页面,就可以看到响应出现401

e6a557795ddadfae9455c8563827451f.png

此时用户只有再次进入登录页面进行登录,才能正常访问页面。

那对于上面注销之后返回的401,实际上比较合理的结果应该是直接跳转到登录页。因此我们还需要在发起请求前对token进行一个判断,如果token不存在,则直接跳转至登录页

这个功能使用vue-router导航守卫来实现。

// 代码位置:/src/router/index.js

// 给路由定义前置的全局守卫
router.beforeEach((to, from, next) => {
    let token = localStorage.getItem('token');
    if(token){
        // token存在 访问login 跳转至产品证书制作页面
        if(to.path == '/' || to.path  == '/login'){
            next('/certMake');
        }else{
            next();
        }
    }else{
       // token不存在  路径'/'就是登录页面设置的path
        if(to.path === '/'){
            next();
        }else{
            next('/')
        }
    }

})

设置登录过期时间

前面我们完成的登录功能,除了注销后需要登录,其他任何时候只要用户成功登录过一次,就不需要在此登录了。这样存在一个很大的安全隐患,那就是当用户的token不慎泄露后,别人是可以没有限制的操作我们的页面。

因此最好的办法就是给token设置一个有效期,当有效期到了以后,强制用户退出登录,在下一次登录的时候生成新的token

ce630c44740208458dc784b41e1c2bf5.png

那接下来就是这个功能的代码实现了。

后端配置token有效期

后端在userAuth模块下新建一个auth.py,自定义一个用户认证类,继承TokenAuthentication,并且实现token过期的处理。

# -*- coding: utf-8 -*-
# Create your views here.

from rest_framework.authentication import TokenAuthentication
from rest_framework import exceptions
from django.utils import timezone
from datetime import timedelta
from django.conf import settings

# token过期时间处理
class ExpiringTokenAuthentication(TokenAuthentication):
    def authenticate_credentials(self, key):
        model = self.get_model()
        try:
            token = model.objects.select_related('user').get(key=key)
        except model.DoesNotExist:
            raise exceptions.AuthenticationFailed('Invalid token.')
        if not token.user.is_active:
            raise exceptions.AuthenticationFailed('User inactive or deleted.')
        # 重点就在这句了,这里做了一个Token过期的验证
        # 如果当前的时间大于Token创建时间+DAYS天,那么就返回Token已经过期
        if timezone.now() > (token.created + timedelta(days=7)):  
            print "Token has expired"
            # 过期以后 响应为401
            raise exceptions.AuthenticationFailed('Token has expired')
        return (token.user, token)

这里设置token的有效期是7天。

接着修改settings.py中配置的全局认证方案为我们自定义的用户认证ExpiringTokenAuthentication

# 设置全局身份认证方案
REST_FRAMEWORK = {
    'DEFAULT_PERMISSION_CLASSES': (
        'rest_framework.permissions.IsAuthenticated',
    ),
    'DEFAULT_AUTHENTICATION_CLASSES': (
        'userAuth.auth.ExpiringTokenAuthentication', # token过期时间
        # 'rest_framework.authentication.TokenAuthentication',  # token认证
    )
}

然后我们在userAuth模块的views.py中定义退出登录的逻辑:退出登录时删除数据库中的token

@api_view(['GET'])
@permission_classes((AllowAny,))
@authentication_classes(())
def logout(request):
    """退出登录"""
    result = True
    errorInfo = u''
    detail = {}
    token = ''
    authInfo = request.META.get('HTTP_AUTHORIZATION')
    if authInfo:
        token = authInfo.split(' ')[1]
    try:
        # 退出登录 删除token
        tokenObj = Token.objects.get(key=token)
        tokenObj.delete()
    except Exception as e:
        traceback.print_exc(e)
        print 'token not exist'
        result = False
        errorInfo = u'退出登录失败'
    return Response({"result": result, "detail": {}, "errorInfo": errorInfo})

前端设置

token过期以后,后端的响应状态码401,因此我们需要在响应拦截器中处理这个401,即当后端响应为401时,就弹框提示用户登录过期,强制用户退出登录

// 代码位置:/src/utils/request.js

import { MessageBox } from 'element-ui';

//响应拦截器
instance.interceptors.response.use(
  response => {
    return response;
  },
  error => {
    // 在这里处理一下token过期的逻辑
    // 后端验证token过期以后 会返回401 
    if(error.response.status == 401){
      MessageBox.confirm('登录过期,请重新登录', '确定登出', {
        confirmButtonText: '重新登录'
        type: 'warning'
      }).then(() => {
        // 调用接口退出登录
        get('/userAuth/logout').then( response => {
          // 移除本地缓存的token
          localStorage.removeItem("token");
          location.reload();
        })
      })
    }
    return Promise.reject(error);
  }
)

结果演示

到这里设置登录过期时间的逻辑就完成了,我们来演示一下最后的结果。

首先我们先看一下数据库中已有的token的创建时间。

1147984262fa945020a28adedf89cfda.png

可以看到数据库中已有的token的创建时间是2020-09-17,现在的时间是2020-10-10号,已经超出token的有效期。

前面设置token的有效期是7天。

然后我们刷新一下页面。

c63e4d39cb2744e573153f73a4e793e8.png

可以看到页面成功弹出了强制用户重新登录

当我们点击重新登录按钮后,就会请求后端的logout接口,数据库中已有的token会被删除,删除成功之后本地缓存在localStorage中的token也会被删除,最后会跳转到产品的登录页面。

e51d194704735c80fee64a7b801c515d.png

数据库中的token已经被删除。

最后在登录页面输入用户名密码重新登录,就会发现数据库中的token已经更新。

f2115a9b4ab932c26052583776f41b9b.png

最后

关于 Vue结合Django-Rest-Framework实现登录认证这个系列的文章就结束了。文章的内容基本都是实战操作,没有太大的难度,希望能给大家一个参考。

猜你想看

Vue结合Django-Rest-Framework实现登录认证(上)

9a9efe6a6a4daa6acf85d232389553af.gif

883e17dfae2dc3a25e754d076cca52db.png

9203c379d9802d90fa25db9c446665ca.gif

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值