作者:小土豆biubiubiu

博客园:https://www.cnblogs.com/HouJiao/

掘金:https://juejin.im/user/2436173500265335

微信公众号:土豆妈的碎碎念(扫码关注,一起吸猫,一起听故事,一起学习前端技术)

作者文章的内容均来源于自己的实践,如果觉得有帮助到你的话,可以点赞️给个鼓励或留下宝贵意见

前言

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

那这一节需要对前面实现的内容进行优化:

1. 优化axios:请求封装、认证信息的封装

2. 注销

3. 设置token过期时间

优化axios

axios的优化就是对axios进行一个封装,单独抽离出来一个模块,负责编写请求的API,在组件中只需要调用这个API传入对应的参数,就能在请求发送的同时实现认证信息的设置

// 代码位置:/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
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发起请求。

// 引入前面封装好的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字段。

注销

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

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

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

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

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

上面描述的功能使用 守卫导航 实现

代码定义在router.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设置一个有效期,当有效期到了以后,强制用户退出登录,在下一次登录的时候重新生成新的token

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

后端配置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天

接着修改setting中配置的全局认证方案为我们自定义的用户认证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时,就弹框提示用户登录过期,强制用户退出登录。

//响应拦截器
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的创建时间。

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

前面设置token的有效期是7

然后我们刷新一下页面。

发现已经成功弹出强制用户重新登录

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

数据库中的token已经被删除

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

最后

关于 《vue结合Django-Rest-Frameword结合实现登录认证》这个系列的文章就结束了。

文章基本都是实战操作,希望可以给大家一个参考。

文章索引

《Vue结合Django-Rest-Frameword结合实现登录认证(一)》

《Vue结合Django-Rest-Frameword结合实现登录认证(二)》

关于

作者

小土豆biubiubiu

一个努力学习的前端小菜鸟,知识是无限的。坚信只要不停下学习的脚步,总能到达自己期望的地方

同时还是一个喜欢小猫咪的人,家里有一只美短小母猫,名叫土豆

博客园

https://www.cnblogs.com/HouJiao/

掘金

https://juejin.im/user/2436173500265335

微信公众号

土豆妈的碎碎念

微信公众号的初衷是记录自己和身边的一些故事,同时会不定期更新一些技术文章

欢迎大家扫码关注,一起吸猫,一起听故事,一起学习前端技术

作者寄语

小小总结,欢迎大家指导~

参考文章

django-rest-framework官方文档#权限篇

django-rest-framework官方文档#授权认证篇

Vue结合Django-Rest-Frameword结合实现登录认证(二)的更多相关文章

  1. Django使用自定义的authentication登录认证

    import ldap class LDAPMgmt(): def __init__(self): self.ldap_host = 'xxx' self.ldap_base_dn = 'ou=xx, ...

  2. Django 之装饰器实现登录认证

    def check_login(func): # 自定义登录验证装饰器 def warpper(request, *args, **kwargs): is_login = request.sessio ...

  3. 二、Django用Eclipse编写一个登录界面

    一.Django用Eclipse编写一个登录界面 二.Django用Eclipse编写一个登录界面Ajax和Django交互 各软件版本:Python 2.7.14,django 1.6.11 原来已 ...

  4. Vue结合Django-Rest-Frameword结合实现登录认证(一)

    作者:小土豆biubiubiu 博客园:https://www.cnblogs.com/HouJiao/ 掘金:https://juejin.im/user/2436173500265335 微信公众 ...

  5. Win10环境前后端分离项目基于Vue.js+Django+Python3实现微信(wechat)扫码支付流程(2021年最新攻略)

    原文转载自「刘悦的技术博客」https://v3u.cn/a_id_182 之前的一篇文章:mpvue1.0+python3.7+Django2.0.4实现微信小程序的支付功能,主要介绍了微信小程序内 ...

  6. 服务器部署 Vue 和 Django 项目的全记录

    本篇记录我在一个全新服务器上部署 Vue 和 Django 前后端项目的全过程,内容包括服务器初始配置.安装 Django 虚拟环境.python web 服务器 uWSGI 和反向代理 Nginx ...

  7. vue+springboot前后端分离实现单点登录跨域问题处理

    最近在做一个后台管理系统,前端是用时下火热的vue.js,后台是基于springboot的.因为后台系统没有登录功能,但是公司要求统一登录,登录认证统一使用.net项目组的认证系统.那就意味着做单点登 ...

  8. 关于django用户登录认证中的cookie和session

    最近弄django的时候在用户登录这一块遇到了困难,网上的资料也都不完整或者存在缺陷. 写这篇文章的主要目的是对一些刚学django的新手朋友提供一些帮助.前提是你对django中的session和c ...

  9. 一、Django用Eclipse编写一个登录界面

    一.Django用Eclipse编写一个登录界面 二.Django用Eclipse编写一个登录界面Ajax和Django交互 Eclipse安装Python插件和Django的步骤直接省略. 创建de ...

随机推荐

  1. Azure Storage 系列(二) .NET Core Web 项目中操作 Blob 存储

    一,引言 上一篇文章,我们介绍到在实际项目中系统会产生大量的日志文件,用户上传的头像等等,同时也介绍到可以使用Azure Blob Storage 来存储项目中的一些日志文件,用户头像,用户视频等等. ...

  2. Zabbix-4.0-设置钉钉报警脚本

    问题:当服务器发生报错时,有一个信息能实现自动发送到我的手机或者应用上,以达到对服务器的实时的监控与处理.邮件与短信不能满足实时性,于是想到了钉钉的通知. 思路:在钉钉里面建一个群,群里面拉个机器人. ...

  3. VMware安装Centos7 -九五小庞

    VMware安装Centos7超详细过程(图文) https://blog.csdn.net/babyxue/article/details/80970526 安装centos7的时候 启动会提示Pl ...

  4. 无法从NVIDA官网下载安装CUDA安装包?NVIDA官网怎么了?

    最近几天由于不知名的原因,导致很多人无法从官网下载NVIDA的CUDA安装包,下载时,浏览器提示此文件可能危害你的计算机,选择保留下载下来也只是一个42字节的exe文件 双击进行安装又出现以下问题: ...

  5. C011:分数相加

    代码: #include "stdafx.h" int _tmain(int argc, _TCHAR* argv[]) { int up1,down1,up2,down2; do ...

  6. 干货:不同场景容器内获取客户端源IP的方法

    摘要:客户端和容器服务器之间可能存在多种不同形式的代理服务器,那容器中如何获取到客户端真实的源ip呢? k8s已经成为当今容器化的标准,人们在享受容器带来的高效与便利的同时,也遇到一些烦恼:客户端和容 ...

  7. jmeter做简单的压测

    一.JMeter概述jmeter除了可以做借口测试外,还可以做压力测试:首先介绍jmeter中各个组件在压力测试中扮演的角色 1)线程(Threads(Users))即虚拟用户,线程组里可设置需要模拟 ...

  8. python模块之----subprocess

    例子 >>> subprocess.getstatusoutput('pwd')(0, '/home/ronny')>>> subprocess.getoutput ...

  9. JAVA知识点 I/O流框架简要笔记

    I/O 框架 流的概念 内存与存储设备之间传输数据的通道 流的分类 按方向[重点] 输入流:将<存储设备>中的内容读到<内存>中 输出流:将<内存>中的内容写到&l ...

  10. HTML自学第一篇

    教程来自W3CSchool 因为笔者有过开发经验 本篇只是个人对HTML自学的笔记,可能不适合用于给他人理解和学习 什么是 HTML HTML 指的是超文本标记语言 (Hyper Text Marku ...