前言
在上一篇 Vue结合Django-Rest-Framework实现登录认证(上)一文中,我们利用token
实现了一个非常简单的用户登录认证
功能。
那这一篇文章来完成上一篇文章结尾处梳理的需要完善的三个功能点。
1.axios优化
2.用户注销
3.设置登录过期时间
axios优化
axios
的优化就是对axios
进行一个封装,单独抽离出来一个模块,负责发送get
和post
请求,同时在请求发送的同时实现认证信息
的设置。
// 代码位置:/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)
})
})
}
可以看到,我们对axios
的get
、post
请求进行了封装,同时我们将认证需要添加到请求头部
的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
字段。
用户注销
当用户需要注销
时,我们应该做的就是清除本地保存的token
。
logout: function(){
// 清除token
localStorage.removeItem("token");
// 跳转至登录页 登录页面在router.js中的配置的path就是‘/’
this.$router.push("/");
}
清除以后呢,如果我们直接在浏览器中手动输入url
进入某个页面,就可以看到响应出现401
。
此时用户只有再次进入登录页面
进行登录,才能正常访问页面。
那对于上面注销
之后返回的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
。
那接下来就是这个功能的代码实现了。
后端配置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
的创建时间。
可以看到数据库中已有的token
的创建时间是2020-09-17
,现在的时间是2020-10-10
号,已经超出token
的有效期。
前面设置
token
的有效期是7
天。
然后我们刷新一下页面。
可以看到页面成功弹出了强制用户重新登录
。
当我们点击重新登录
按钮后,就会请求后端的logout
接口,数据库中已有的token
会被删除,删除成功之后本地缓存在localStorage
中的token
也会被删除,最后会跳转到产品的登录页面。
数据库中的
token
已经被删除。
最后在登录页面输入用户名
和密码
重新登录,就会发现数据库中的token
已经更新。
最后
关于 Vue结合Django-Rest-Framework实现登录认证
这个系列的文章就结束了。文章的内容基本都是实战操作,没有太大的难度,希望能给大家一个参考。
猜你想看
Vue结合Django-Rest-Framework实现登录认证(上)