Vue結合Django-Rest-Frameword結合實現登錄認證(二)
作者:小土豆biubiubiu
部落格園://www.cnblogs.com/HouJiao/
掘金://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官網://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)
})
})
}
可以看到,我們對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發起請求。
// 引入前面封裝好的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
一個努力學習的前端小菜鳥,知識是無限的。堅信只要不停下學習的腳步,總能到達自己期望的地方
同時還是一個喜歡小貓咪的人,家裡有一隻美短小母貓,名叫土豆
部落格園
掘金
//juejin.im/user/2436173500265335
微信公眾號
土豆媽的碎碎念
微信公眾號的初衷是記錄自己和身邊的一些故事,同時會不定期更新一些技術文章
歡迎大家掃碼關注,一起吸貓,一起聽故事,一起學習前端技術
作者寄語
小小總結,歡迎大家指導~
參考文章
👉 django-rest-framework官方文檔#許可權篇
👉 django-rest-framework官方文檔#授權認證篇

