1、前端訪問控制的常規處理方法
前端訪問控制,一般針對介面元素dom element進行可見屬性或enable屬性進行控制,有許可權的,相關元素可見或使能;沒許可權的,相關元素不可見或失能。這樣使用者可以明確哪些是無權訪問的。可見屬性要比使能屬性更廣泛,這是每個dom元素都有的屬性。
當然前端控制僅僅是整體訪問控制的一部分,後端還需要進一步針對介面訪問進行鑑權。因為通過編輯瀏覽器的介面元素的屬性,可以繞過前端控制。
在Vue中,也有通過控制路由來實現訪問控制的,但沒有控制介面元素的情況下,使用者體驗不是很好。
本文給出了Vue框架下前端訪問控制的整體方案。
2、總體方案
在使用者登入時,或許可權變更時,後端通過介面將許可權樹發給前端。為了減少不必要的資料傳輸,後端發出的許可權樹僅包括有許可權的功能項,即前端收到的許可權樹的各個節點都是有許可權的功能項。
許可權樹節點的資料部分即為功能項的許可權資訊,包括兩個關鍵欄位:url和domKey。url是後端自己使用,在AOP鑑權切面類中,攔截非法的介面訪問。domKey是給前端使用的,即dom element的id值,domKey的確定需要前後端協商一致,不能搞錯。
domKey在同一個路徑上,不允許重複;不同路徑,允許重複。所謂路徑,是從根節點開始,到該節點的一系列節點組成的樹杈。當然,沒有必要的話,domKey最好不重複。同一個介面檢視範圍的各子節點的domKey也不允許重複。
前端本地儲存使用者token和許可權樹JSON字串,如果本地這個儲存資訊存在,重新開啟瀏覽器,可以免登入。(僅本地token有效,不能完全保證token真的有效,如後端重啟伺服器、token過期等導致token失效,前端通過HTTP訪問時,仍然會跳到登入頁面)。
登入成功後,將token和許可權樹JSON字串儲存到本地儲存。
許可權發生變更時,通過response攔截器,檢查有無附加資訊,如有需要,更新token和許可權樹JSON字串。
前端開發一個許可權樹的管理的js檔案,用於許可權樹JSON物件的訪問,許可權樹JSON字串被轉換成許可權樹JSON物件。
開發前端頁面vue檔案時,需要進行許可權控制的dom element,使用下列屬性:
class="permissions" id="相關domKey"
通過class來標識該介面元素是與訪問控制相關的,目的是確定需要進行許可權控制的元件範圍,id即為該功能項對應的domKey。
然後,使用一個公共許可權設定方法,來統一處理許可權相關的介面元素。
由於Vue的元件style,可以有scoped屬性設定,此時,在App.vue中,就不能訪問到相關dom element的class,區域性式樣渲染後,在外部被改寫,因此,在scoped限制的情況下,需要在scoped起作用的Vue元件中,也要呼叫公共許可權設定方法。另外,scoped的限制,恰好使得相同domKey的節點,可以通過上級節點domKey來加以區分。這樣,就用統一的方法,實現了前端頁碼的訪問控制。
3、方案實現
3.1、功能項的表結構定義
DROP TABLE IF EXISTS `function_tree`;
CREATE TABLE `function_tree`
(
`func_id` INT(11) NOT NULL DEFAULT 0 COMMENT '功能ID',
`func_name` VARCHAR(100) NOT NULL DEFAULT '' COMMENT '功能名稱',
`parent_id` INT(11) NOT NULL DEFAULT 0 COMMENT '父功能ID',
`level` TINYINT(4) NOT NULL DEFAULT 0 COMMENT '功能所在層級',
`order_no` INT(11) NOT NULL DEFAULT 0 COMMENT '顯示順序',
`url` VARCHAR(80) NOT NULL DEFAULT '' COMMENT '訪問介面url',
`dom_key` VARCHAR(80) NOT NULL DEFAULT '' COMMENT 'dom物件的id',
`remark` VARCHAR(200) NOT NULL DEFAULT '' COMMENT '備註',
-- 記錄操作資訊
`operator_name` VARCHAR(80) NOT NULL DEFAULT '' COMMENT '操作人賬號',
`delete_flag` TINYINT(4) NOT NULL DEFAULT 0 COMMENT '記錄刪除標記,1-已刪除',
`create_time` DATETIME(3) NOT NULL DEFAULT NOW(3) COMMENT '建立時間',
`update_time` DATETIME(3) DEFAULT NULL ON UPDATE NOW(3) COMMENT '更新時間',
PRIMARY KEY (`func_id`)
) ENGINE = InnoDB
DEFAULT CHARSET = utf8 COMMENT ='功能表';
如有需要,可以增加icon欄位,用於前端樹節點的顯示。
3.2、後端許可權樹的輸出
後端在登入成功後,給前端傳送token和許可權樹JSON字串。
關於樹節點的生成,可參閱:Java通用樹結構資料管理---https://www.cnblogs.com/alabo1999/p/14928380.html。裡面有關於許可權樹的例子。
為了方便前端管理,這裡修改許可權樹的輸出,將根節點也一併輸出到前端。
在管理員修改使用者許可權後,動態許可權更新,可通過附加資訊,給前端傳送token和許可權樹JSON字串。參閱:Spring Boot動態許可權變更實現的整體方案---https://www.cnblogs.com/alabo1999/p/14948914.html。
3.3、前端本地快取
vue專案中,新建/src/store目錄,建立inde.js檔案。程式碼如下:
import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex);
const store = new Vuex.Store({
state: {
// 儲存token
token: localStorage.getItem('token') ? localStorage.getItem('token') : '',
// 儲存許可權樹
rights: localStorage.getItem('rights') ? localStorage.getItem('rights') : ''
},
mutations: {
// 修改token,並將token存入localStorage
changeLogin (state, user) {
if(user.token){
state.token = user.token;
localStorage.setItem('token', user.token);
}
if (user.rights){
state.rights = user.rights;
localStorage.setItem('rights', user.rights);
}
}
}
});
export default store;
3.4、建立許可權管理模組
vue專案中,新建/src/common目錄,建立treeNode.js檔案。程式碼如下:
/**
* 處理樹結構資料,這裡主要指功能許可權樹
* 許可權樹的結構如下:
* [
* {
* nodeData:{
* funcId:1, //功能ID
* funcName:"", //功能名稱
* parentId:0, //父節點ID
* level:1, //功能所在層級
* orderNo:2, //顯示順序
* url:"", //訪問介面url
* domKey:"" //dom物件的id
* },
* children:[
* nodeData:{...},
* children:[...]
* ]
* },
* {
* nodeData:{...},
* children:[...]
* }
* ]
*/
var TreeNode = {
//功能樹
rightsTree:null,
/**
* 將許可權樹的JSON字串載入到樹物件上
* @param {許可權樹的JSON字串} rights
*/
loadData(rights){
//將快取的JSON字串,轉為JSON物件,為一級樹節點的陣列
var treeNode = JSON.parse(rights);
return treeNode;
},
/**
* 在給定樹上,找到上級domkey為superDomkey的給定domKey的樹節點
* 不同子樹如果存在子節點domKey重複的情況,也可以區分
* @param {給定樹節點} rightsTree
* @param {上級的domkey} superDomkey
* @param {樹節點的domkey} domKey
*/
lookupNodeByDomkeys(rightsTree,superDomkey,domKey){
var node = null;
var superNode = null;
//先尋找superDomkey
if(superDomkey != ""){
//如果上級物件的domkey非空
superNode = this.lookupNodeByDomkey(rightsTree,superDomkey);
}
if (superNode != null){
//如果上級節點非空,或已找到,則在子樹上搜尋,可加快搜尋速度,並且可避免子節點domKey重複的情況
node = this.lookupNodeByDomkey(superNode,domKey);
}else{
node = this.lookupNodeByDomkey(rightsTree,domKey);
}
return node;
},
/**
* 在給定的子樹中,搜尋指定domKey的樹節點
* @param {子樹} rightsTree
* @param {domkey} domKey
*/
lookupNodeByDomkey(rightsTree,domKey){
var node = null;
var functionInfo = rightsTree.nodeData;
//先查詢自身的資料
if (functionInfo.domKey == domKey){
//如果找到,則返回
return rightsTree;
}
//搜尋子節點
for (var i = 0; i < rightsTree.children.length; i++){
var item = rightsTree.children[i];
node = this.lookupNodeByDomkey(item,domKey);
if (node != null){
break;
}
}
return node;
}
}
export default TreeNode;
如果domKey確保唯一的話,使用Map可能是訪問效率更高的方案。這裡還是使用樹型結構來管理許可權樹。
3.5、建立公共方法模組
vue專案中,在/src/common目錄下,建立commonFuncs.js檔案。程式碼如下:
import TreeNode from './treeNode.js'
var commonFuncs = {
checkRights(superDomkey){
//先載入許可權樹
if (TreeNode.rightsTree == null){
let rights = localStorage.getItem('rights');
if (rights === null || rights === ''){
//沒有許可權樹
return;
}
//載入許可權樹
TreeNode.rightsTree = TreeNode.loadData(rights);
}
//獲取class包含permissions的所有dom物件
var elements = document.getElementsByClassName('permissions');
for(var i = 0; i < elements.length; i++){
var element = elements[i];
if (element.id != undefined)
{
var node = null;
//如果物件有id,檢查許可權
if (superDomkey == null || superDomkey == undefined){
//如果未指定上級domkey,直接查詢
node = TreeNode.lookupNodeByDomkey(TreeNode.rightsTree,element.id);
}else{
//指定上級domkey
node = TreeNode.lookupNodeByDomkeys(TreeNode.rightsTree,superDomkey,element.id)
}
if (node != null && node != undefined){
//包含節點
if (element.style.display == "none"){
element.style.display = "";
}
console.log('has rights :'+element.id);
}else{
element.style.display="none";
console.log('has not rights :'+element.id);
}
}
}
}
};
export default commonFuncs;
checkRights方法,引數為superDomkey,即指定上級節點的domKey,允許為空或空串,相當於不指定。其查詢當前頁面或scoped範圍的文件中,class名稱包含permissions的所有dom元素。取得dom的id,即功能節點的domKey,如果在許可權樹中存在對應節點,則表示有許可權;否則表示無許可權。(注意:前端的許可權樹都是有許可權的功能節點)。
3.6、修改main.js
修改main.js檔案,使得公共模組生效。程式碼如下:
// The Vue build version to load with the `import` command
// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
import Vue from 'vue'
import App from './App'
import router from './router'
import store from './store'
import ElementUI from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css'
import md5 from 'js-md5';
import axios from 'axios'
import VueAxios from 'vue-axios'
import TreeNode_ from './common/treeNode.js'
import CommonFuncs_ from './common/commonFuncs.js'
import instance_ from './api/index.js'
import global_ from '../config/global.js'
Vue.use(VueAxios,axios)
Vue.prototype.$md5 = md5
Vue.prototype.TreeNode = TreeNode_
Vue.prototype.$baseUrl = process.env.API_ROOT
Vue.prototype.instance = instance_ //axios例項
Vue.prototype.global = global_
Vue.prototype.commonFuncs = CommonFuncs_
Vue.use(ElementUI)
Vue.config.productionTip = false
/* eslint-disable no-new */
var vue = new Vue({
el: '#app',
router,
store,
components: { App },
template: '<App/>',
render:h=>h(App)
})
export default vue
引入了commonFuncs和TreeNode全域性物件,可以在vue檔案中使用。
3.7、元件示例
側邊導航欄,與許可權控制相關,可以作為示例。檔案為Left.vue,程式碼如下:
<template>
<div class="left-sidebar">
<el-menu :default-openeds="['1']" style="background:#F0F6F6;">
<el-submenu index="1">
<el-menu-item-group >
<el-menu-item index="1-1">
<router-link class="menu" tag="li" to="/home" exact-active-class="true"
id="homeMenu" active-class="_active">
<i class="el-icon-s-home"></i>首頁
</router-link>
</el-menu-item>
<el-submenu index="1-2" id="userManagementMain">
<template slot="title" ><i class="el-icon-user-solid"></i>使用者管理</template>
<el-menu-item index="1-2-1" class="permissions" id="userManagementSub">
<router-link class="menu" tag="li" to="/userManagement">
<i class="el-icon-user"></i>使用者管理
</router-link>
</el-menu-item>
<el-menu-item index="1-2-2" class="permissions" id="changePassword">
<router-link class="menu"tag="li" to="/changePassword">
<i class="el-icon-key"></i>修改密碼
</router-link>
</el-menu-item>
</el-submenu>
<el-menu-item index="1-3" class="permissions" id="questionnaireManagement">
<router-link class="menu" tag="li" to="/questionnaireManagement">
<i class="el-icon-document"></i>問卷內容管理
</router-link>
</el-menu-item>
<el-submenu index="1-4" class="permissions" id="issueManagementMain">
<template slot="title"><i class="el-icon-message"></i>問卷釋出管理</template>
<el-menu-item index="1-4-1" class="permissions" id="issueManagementSub">
<router-link class="menu" tag="li" to="/issueManagement">
<i class="el-icon-phone"></i>釋出問卷查詢
</router-link>
</el-menu-item>
<el-menu-item index="1-4-2" class="permissions" id="issueTaskQuery">
<router-link class="menu" tag="li" to="/issueTaskQuery">
<i class="el-icon-tickets"></i>釋出任務查詢
</router-link>
</el-menu-item>
</el-submenu>
<el-menu-item index="1-5" class="permissions" id="answerSheetManagement">
<router-link class="menu" tag="li" to="/answerSheetManagement">
<i class="el-icon-receiving"></i>答卷管理
</router-link>
</el-menu-item>
</el-menu-item-group>
</el-submenu>
</el-menu>
</div>
</template>
<style>
/* 去掉右邊框 */
.el-menu {
border-right: none;
}
.el-submenu {
background-color: rgb(231, 235, 220) ;
}
</style>
注意那些:class="permissions" id=“XXX”的dom元素,基本都是el-menu-item。這裡,將scoped去掉了,因為選單項,目前只有側邊導航欄在使用。
3.7、修改App.vue
App.vue,作為應用頁面元件的總成,在裡面進行總的許可權控制。程式碼如下:
<template>
<div id="app">
<!-- 其他頁 -->
<el-container style="min-height: calc(100% - 50px);" v-if="$route.meta.keepAlive">
<!-- 無頭部導航欄 -->
<el-container>
<el-aside :style="{width:collpaseWidth}">
<!-- 側邊欄 -->
<keep-alive>
<left></left>
</keep-alive>
</el-aside>
<el-main>
<!-- Body -->
<router-view></router-view>
</el-main>
</el-container>
<!-- 無足部 -->
</el-container>
<!-- 登入頁 -->
<router-view v-if="!$route.meta.keepAlive"></router-view>
</div>
</template>
<script>
import left from './components/Left.vue'
export default {
name: 'App',
components: {
left: left
},
data(){
return {
collpaseWidth:200
}
},
mounted:function(){
this.commonFuncs.checkRights();
},
methods: {
}
}
</script>
<style>
#app {
font-family: 'Avenir', Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>
在頁碼載入時,呼叫commonFuncs.checkRights()方法,進行許可權控制。
3.8、測試一下
3.8.1、獲取許可權樹資料
登入成功後,後端輸出的許可權樹資料如下:
{
rights = {
"nodeData": {
"funcId": 0,
"funcName": "root",
"parentId": -1,
"level": 0,
"orderNo": 0,
"url": "",
"domKey": ""
},
"children": [{
"nodeData": {
"funcId": 1,
"funcName": "使用者管理一級選單",
"parentId": 0,
"level": 1,
"orderNo": 0,
"url": "",
"domKey": "userManagementMain"
},
"children": [{
"nodeData": {
"funcId": 3,
"funcName": "修改密碼",
"parentId": 1,
"level": 2,
"orderNo": 1,
"url": "/userMan/changePassword",
"domKey": "changePassword"
},
"children": []
}]
}, {
"nodeData": {
"funcId": 10,
"funcName": "問卷內容管理一級選單",
"parentId": 0,
"level": 1,
"orderNo": 1,
"url": "",
"domKey": "questionnaireManagement"
},
"children": [{
"nodeData": {
"funcId": 11,
"funcName": "新增問卷",
"parentId": 10,
"level": 2,
"orderNo": 0,
"url": "/questionnaireMan/addQuestionnaire",
"domKey": "addQuestionnaire"
},
"children": []
}, {
"nodeData": {
"funcId": 12,
"funcName": "編輯問卷",
"parentId": 10,
"level": 2,
"orderNo": 1,
"url": "/questionnaireMan/editQuestionnaire",
"domKey": "editQuestionnaire"
},
"children": []
}, {
"nodeData": {
"funcId": 13,
"funcName": "查詢問卷",
"parentId": 10,
"level": 2,
"orderNo": 2,
"url": "/questionnaireMan/queryQuestionnaires",
"domKey": "queryQuestionnaire"
},
"children": []
}, {
"nodeData": {
"funcId": 14,
"funcName": "複製新建問卷",
"parentId": 10,
"level": 2,
"orderNo": 3,
"url": "",
"domKey": "copyAddQuestionnaire"
},
"children": []
}, {
"nodeData": {
"funcId": 15,
"funcName": "瀏覽問卷",
"parentId": 10,
"level": 2,
"orderNo": 4,
"url": "/questionnaireMan/previewQuestionnaire",
"domKey": "browseQuestionnaire"
},
"children": []
}, {
"nodeData": {
"funcId": 16,
"funcName": "提交稽核",
"parentId": 10,
"level": 2,
"orderNo": 5,
"url": "/questionnaireMan/submitAduit",
"domKey": "submitAudit"
},
"children": []
}, {
"nodeData": {
"funcId": 18,
"funcName": "作廢問卷",
"parentId": 10,
"level": 2,
"orderNo": 7,
"url": "/questionnaireMan/cancelQuestionnaire",
"domKey": "cancelQuestionnaire"
},
"children": []
}]
}, {
"nodeData": {
"funcId": 20,
"funcName": "問卷釋出管理一級選單",
"parentId": 0,
"level": 1,
"orderNo": 2,
"url": "",
"domKey": "issueManagementMain"
},
"children": [{
"nodeData": {
"funcId": 21,
"funcName": "釋出管理二級選單",
"parentId": 20,
"level": 2,
"orderNo": 0,
"url": "",
"domKey": "issueManagementSub"
},
"children": []
}, {
"nodeData": {
"funcId": 22,
"funcName": "釋出任務查詢",
"parentId": 20,
"level": 2,
"orderNo": 1,
"url": "",
"domKey": "issueTaskQuery"
},
"children": []
}]
}, {
"nodeData": {
"funcId": 40,
"funcName": "答卷管理一級選單",
"parentId": 0,
"level": 1,
"orderNo": 3,
"url": "",
"domKey": "answerSheetManagement"
},
"children": [{
"nodeData": {
"funcId": 41,
"funcName": "查詢答卷記錄",
"parentId": 40,
"level": 2,
"orderNo": 0,
"url": "/answerSheetMan/queryAnswerTask",
"domKey": "queryAnswerSheet"
},
"children": []
}, {
"nodeData": {
"funcId": 42,
"funcName": "回收記錄明細",
"parentId": 40,
"level": 2,
"orderNo": 1,
"url": "/answerSheetMan/getAnswerSubmitDetail",
"domKey": "recoveryDetail"
},
"children": []
}, {
"nodeData": {
"funcId": 43,
"funcName": "答卷統計",
"parentId": 40,
"level": 2,
"orderNo": 2,
"url": "/answerSheetMan/queryStatResult",
"domKey": "answerSheetStat"
},
"children": []
}, {
"nodeData": {
"funcId": 44,
"funcName": "答卷原始記錄",
"parentId": 40,
"level": 2,
"orderNo": 3,
"url": "/answerSheetMan/queryOriginalAnswer",
"domKey": "queryOriginalAnswer"
},
"children": []
}]
}]
}, token = 873820BA39E64005BCCE3E54A830AB2C
}
這些功能項中,有些與導航欄有關,還有一些是頁面的按鈕或連結,在示例中沒有用到。
3.8.2、製作首頁
製作一個簡單的首頁Home.vue,程式碼如下:
<template>
<div id="home">
<h4>歡迎使用</h4>
<h3>XX系統</h3>
</div>
</template>
3.8.3、簡單設定路由導航檔案
修改/src/router/index.js檔案,程式碼如下:
import Vue from 'vue'
import Router from 'vue-router'
import HelloWorld from '@/components/HelloWorld'
import Home from '@/components/Home.vue'
import Login from '@/components/login/Login.vue'
Vue.use(Router)
const router = new Router({
routes: [
{
path: '/home',
name: 'home',
component: Home,
meta: {
keepAlive: true
}
},
{
path: '/login',
name: 'login',
component: Login,
meta: {
keepAlive: false
}
},
]
})
// 導航守衛
// 使用 router.beforeEach 註冊一個全域性前置守衛,判斷使用者是否登陸
router.beforeEach((to, from, next) => {
if (to.path === '/login') {
next();
} else {
let token = localStorage.getItem('token');
if (token === null || token === '') {
next('/login');
} else {
if (to.path === '/'){
next('/home');
}else{
next();
}
}
}
});
export default router;
3.8.4、導航欄效果測試
現在執行Vue,"npm run dev",然後顯示首頁,並用F12顯示調式資訊:
側邊欄頁面顯示如下:
瀏覽器的偵錯程式的控制檯輸出資訊為:
說明,domKey為userMangementSub的dom元素沒有操作許可權,與側邊欄的效果一致。
3.8.5、scoped情況測試
Login.vue,使用了scoped,作為示例,現在將登入按鈕,進行許可權控制,修改如下:
<el-form-item>
<el-button type="primary" class="permissions" id="login" style="width:160px" @click="submitForm('form')">登入</el-button>
</el-form-item>
在Login.vue的script的mounted方法中,增加許可權控制程式碼:
mounted:function(){
//頁面載入時,顯示驗證碼
this.getVerifyCode();
this.commonFuncs.checkRights();
},
由於domKey為login的,沒有在許可權樹中,故其加入許可權控制集合,又沒有被授權,則該按鈕應該不可見。
執行測試,顯示登入頁,效果圖如下:
登入按鈕不可見了,與預期效果一致。
3.9、登入成功後儲存資訊
登入成功後,將後端發生過來的token和許可權樹儲存起來,並將JSON字串轉為JSON物件。
程式碼如下:
submitForm(formName) {
let _this = this;
this.$refs[formName].validate(valid => {
// 驗證通過為true,有一個不通過就是false
if (valid) {
// 通過的邏輯
let passwd = this.$md5(this.form.password);
this.instance.userLogin(this.$baseUrl,{
loginName:_this.form.username,
password:passwd,
verifyCode:_this.form.verifyCode
}).then(res => {
console.log(res.data);
if (res.data.code == this.global.SucessRequstCode){
//如果登入成功
_this.userToken = res.data.data.token;
_this.rights = res.data.data.rights;
//更新許可權樹
this.TreeNode.rightsTee = this.TreeNode.loadData(_this.rights);
console.log(this.TreeNode.rightsTee)
// 將使用者token和許可權樹儲存到vuex中
_this.changeLogin({ token: _this.userToken, rights: _this.rights});
_this.$router.push('/home');
//alert('登陸成功');
}else{
alert(res.data.message);
}
}).catch(error => {
alert('賬號或密碼錯誤');
console.log(error);
});
} else {
console.log('驗證失敗');
return false;
}
});
},
3.10、許可權動態更新的攔截處理
根據許可權動態更新方案,管理員修改使用者許可權後,該使用者第一次訪問後端介面,返回資訊中可能會攜帶附加資訊。這個可能在任何返回JSON格式資料的介面中發生。因此,可使用攔截器,來進行統一處理。
import axios from 'axios';
import router from '../router'
import Vue from 'vue';
import Vuex from 'vuex';
import TreeNode from '../common/treeNode.js'
const instance = axios.create({
timeout: 60000,
headers: {
'Content-Type': "application/json;charset=utf-8"
}
});
//token相關的response攔截器
instance.interceptors.response.use(response => {
if (response) {
switch (response.data.code) {
case 3: //token為空
case 4: //token過期
case 5: //token不正確
localStorage.clear(); //刪除使用者資訊
//要跳轉登陸頁
alert('token失效,請重新登入!');
router.replace({
path: '/login',
});
break;
default:
break;
}
if(response.data.additional){
//如果包含附加資訊
var data = {};
if(response.data.additional.token){
//如果包含token
data.token = response.data.additional.token;
localStorage.setItem('token', data.token);
}
if(response.data.additional.rights) {
data.rights = response.data.additional.rights;
localStorage.setItem('rights', data.rights);
//重新整理許可權樹
TreeNode.rightsTree = TreeNode.loadData(data.rights);
}
}
}
return response;
}, error => {
return Promise.reject(error.response.data.message) //返回介面返回的錯誤資訊
})