程式碼太醜,不建議照搬,可以看RBAC 的實作思路,這是通用的。
git clone https://github.com/wjkang/d2-admin-pm.git
npm install
npm start
需要後端mock服務的支持
git clone https://github.com/wjkang/d2-admin-server.git
npm install
npm start
main.js
中created
的內容轉到router/index.js
內,加入相關邏輯
修改了axios相關程式碼plugin/axios/index.js
,支援介面級權限控制,並且支援配置loading效果
vuex store新增menu模組新增fullAside ,完整路徑store.state.d2admin.menu.fullAside
vuex store中新增permission模組,儲存使用者俱備的功能權限碼,角色編碼,具備存取權限的介面以及是否管理員標識
export default {
namespaced : true ,
state : {
//功能编码
functions : [ ] ,
//角色编码
roles : [ ] ,
//接口
interfaces : {
GET : [ ] ,
POST : [ ] ,
PUT : [ ] ,
DELETE : [ ]
} ,
//是否管理员
isAdmin : false
} ,
mutations : {
set ( state , data ) {
state . functions = data . functions ;
state . roles = data . roles ;
state . isAdmin = data . isAdmin ;
state . interfaces = data . interfaces ;
}
}
}
vuex store中的account
模組的load
action的這部分:
// DB -> store 持久化数据加载上次退出时的多页列表
await dispatch ( 'd2admin/page/openedLoad' , null , { root : true } )
轉到router/index.js
內,需要在載入完權限路由後才執行
菜单
與功能
,一個選單下可以有多個功能,菜单
類型的permission
欄位標識存取這個選單需要的功能權限,功能
類型的permission
欄位相當於此功能的別稱,所以菜单
類型的permission
欄位為其某個功能
類型子節點的permission
值permission
欄位過濾出使用者所能存取的路由使用d2admin
的原有登入邏輯,全域路由守衛中判斷是否已拉取權限訊息,取得後標識為已獲取。
後端需要傳回的權限資訊包括權限過濾後的角色編碼集合,功能編碼集合,介面資訊集合,選單列表,路由列表,以及是否系統管理員識別。格式如下
{
"statusCode" : 200 ,
"msg" : "" ,
"data" : {
"userName" : "MenuManager" ,
"userRoles" : [
"R_MENUADMIN"
] ,
"userPermissions" : [
"p_menu_view" ,
"p_menu_edit" ,
"p_menu_menu"
] ,
"accessMenus" : [
{
"title" : "系统" ,
"path" : "/system" ,
"icon" : "cogs" ,
"children" : [
{
"title" : "系统设置" ,
"icon" : "cogs" ,
"children" : [
{
"title" : "菜单管理" ,
"path" : "/system/menu" ,
"icon" : "th-list"
}
]
} ,
{
"title" : "组织架构" ,
"icon" : "pie-chart" ,
"children" : [
{
"title" : "部门管理" ,
"icon" : "html5"
} ,
{
"title" : "职位管理" ,
"icon" : "opencart"
}
]
}
]
}
] ,
"accessRoutes" : [
{
"name" : "System" ,
"path" : "/system" ,
"component" : "layoutHeaderAside" ,
"componentPath" : "layout/header-aside/layout" ,
"meta" : {
"title" : "系统设置" ,
"cache" : true
} ,
"children" : [
{
"name" : "MenuPage" ,
"path" : "/system/menu" ,
"component" : "menu" ,
"componentPath" : "pages/sys/menu/index" ,
"meta" : {
"title" : "菜单管理" ,
"cache" : true
}
} ,
{
"name" : "RoutePage" ,
"path" : "/system/route" ,
"component" : "route" ,
"componentPath" : "pages/sys/route/index" ,
"meta" : {
"title" : "路由管理" ,
"cache" : true
}
} ,
{
"name" : "RolePage" ,
"path" : "/system/role" ,
"component" : "role" ,
"componentPath" : "pages/sys/role/index" ,
"meta" : {
"title" : "角色管理" ,
"cache" : true
}
} ,
{
"name" : "UserPage" ,
"path" : "/system/user" ,
"component" : "user" ,
"componentPath" : "pages/sys/user/index" ,
"meta" : {
"title" : "用户管理" ,
"cache" : true
}
} ,
{
"name" : "InterfacePage" ,
"path" : "/system/interface" ,
"component" : "interface" ,
"meta" : {
"title" : "接口管理"
}
}
]
}
] ,
"accessInterfaces" : [
{
"path" : "/menu/:id" ,
"method" : "get"
} ,
{
"path" : "/menu" ,
"method" : "get"
} ,
{
"path" : "/menu/save" ,
"method" : "post"
} ,
{
"path" : "/interface/paged" ,
"method" : "get"
}
] ,
"isAdmin" : 0 ,
"avatarUrl" : "https://api.adorable.io/avatars/85/[email protected]"
}
}
將固定選單( /menu/header
、 /menu/aside
)與後端回傳的權限選單( accessMenus
)合併後,存入對應的vuex store模組中
...
let allMenuAside = [ ... menuAside , ... permissionMenu ]
let allMenuHeader = [ ... menuHeader , ... permissionMenu ]
. . .
// 设置顶栏菜单
store . commit ( 'd2admin/menu/headerSet' , allMenuHeader )
// 设置侧边栏菜单
store . commit ( 'd2admin/menu/fullAsideSet' , allMenuAside )
// 初始化菜单搜索功能
store . commit ( 'd2admin/search/init' , allMenuHeader )
預設使用routerMapComponents
的方式處理後端回傳的權限路由
//处理动态添加的路由
const formatRoutes = function ( routes ) {
routes . forEach ( route => {
route . component = routerMapComponents [ route . component ]
if ( route . children ) {
formatRoutes ( route . children )
}
} )
}
. . .
formatRoutes ( permissionRouter )
//动态添加路由
router . addRoutes ( permissionRouter ) ;
// 处理路由 得到每一级的路由设置
store . commit ( 'd2admin/page/init' , [ ... frameInRoutes , ... permissionRouter ] )
路由處理方式及差異可看後面的相關文章
將角色編碼集合,功能編碼集合,介面資訊集合,以及是否系統管理員識別存入對應的vuex store模組中
...
permission . functions = userPermissionInfo . userPermissions
permission . roles = userPermissionInfo . userRoles
permission . interfaces = util . formatInterfaces ( userPermissionInfo . accessInterfaces )
permission . isAdmin = userPermissionInfo . isAdmin == 1
. . .
// 设置权限信息
store . commit ( 'd2admin/permission/set' , permission )
支援使用角色編碼,功能編碼以及介面權限進行控制,如下
export function getMenuList ( ) {
return request ( {
url : '/menu' ,
method : 'get' ,
interfaceCheck : true ,
permission : [ "p_menu_view" ] ,
loading : {
type : 'loading' ,
options : {
fullscreen : true ,
lock : true ,
text : '加载中...' ,
spinner : 'el-icon-loading' ,
background : 'rgba(0, 0, 0, 0.8)'
}
} ,
success : {
type : 'message' ,
options : {
message : '加载菜单成功' ,
type : 'success'
}
}
} )
}
interfaceCheck: true
表示使用介面權限進行控制,如果vuex store中儲存的介面資訊與目前要要求的介面想要匹配,則可發起請求,否則請求將被攔截。
permission:["p_menu_view"]
表示使用角色編碼和功能編碼進行權限校驗,如果vuex store中儲存的角色編碼或功能編碼與目前表示的編碼相匹配,則可發起請求,否則請求將被攔截。
原始碼位置在libs/permission.js
,可依自己需求進行修改
loading
配置相關原始碼在libs/loading.js
,依照自己需求進行配置, success
也是如此,原始碼在libs/loading.js
。 照此思路可以自行配置其它功能,例如請求失敗等。
使用指令v-permission
:
< el-button
v-permission:function.all =" ['p_menu_edit'] "
type =" primary "
icon =" el-icon-edit "
size =" mini "
@click =" batchEdit "
>批量编辑</ el-button >
參數可為function
、 role
,表示以功能編碼或角色編碼進行校驗,為空則使用兩者進行校驗。
修飾符all
,表示必須全部符合指令值中所有的編碼。
原始碼位置在plugin/permission/index.js
,根據自己實際需求進行修改。
使用v-if
+全域方法:
< el-button
v-if =" canAdd "
type =" primary "
icon =" el-icon-circle-plus-outline "
size =" mini "
@click =" add "
>添加</ el-button >
data ( ) {
return {
canAdd : this . hasPermissions ( [ "p_menu_edit" ] )
} ;
} ,
預設同時使用角色編碼與功能編碼進行校驗,有一項匹配即可。
類似的方法還要hasFunctions
, hasRoles
。
原始碼位置在plugin/permission/index.js
,根據自己實際需求進行修改。
不要使用
v-if="hasPermissions(['p_menu_edit'])"
這種方式,會導致方法多次執行
也可以直接在元件中從vuex store讀取權限資訊進行校驗。
頁面層級的元件放到pages/
目錄下,並且在routerMapCompnonents/index.js
中以key-value的形式匯出
不需要權限控制的固定選單放到menu/aside.js
和menu/header.js
中
不需要權限控制的路由放到router/routes.js
frameIn
內
需要權限控制的選單與路由通過介面的管理功能進行添加,確保選單的path
與路由的path
相對應,路由的name
與頁面元件的name
一致才能使keep-alive
生效,路由的component
在routerMapCompnonents/index.js
中能透過key配對到。
開發階段選單與路由的新增功能可由開發人員自行維護,並維護一份清單,上線後將清單交給相關的人去維護即可。
如果覺得麻煩,不想選單與路由由後端返回,可以在前端維護一份選單和路由(路由中的
component
還是使用字串,參考mock/permissionMenuAndRouter.js
),並且在選單和路由上面維護對應的權限編碼,一般都是使用功能編碼。後端就不需要返回選單和路由資訊了,但是其他權限訊息,例如角色編碼,功能編碼等還是需要的。透過後端傳回的功能編碼列表,在前端過濾出使用者俱備權限的選單和路由,過濾處理後後的選單與路由格式與先前由後端傳回的格式一致,然後將處理後的選單與路由當做後端回傳的一樣處理即可。
資料mock使用lazy-mock修改而來的d2-admin-server,資料真實來自後端,相較於其他工具,支援資料持久化,儲存使用的是json文件,不需要安裝資料庫。簡單的設定即可自動產生增刪改查的介面。詳細用法可看lazy-mock文檔
後端使用中間件控制存取權限,例如:
. get ( '/menu' , PermissionCheck ( ) , controllers . menu . getMenuList )
PermissionCheck
預設使用介面進行校驗,校驗使用者所能存取的API中是否符合目前API,支援使用功能編碼與角色編碼進行校驗PermissionCheck(["p_menu_edit"],["r_menu_admin"],true)
,第一個參數為功能編碼,第二個為角色編碼,第三個為是否使用介面進行校驗。
前端程式碼生成還在開發中...
vue權限路由實作方式總結vue權限路由實作方式總結二企業管理系統前後端分離架構設計系列一權限模型篇