zhcc基于 vue+ ssm + shiro 的權(quán)限框架
zhcc
基于vue(element ui) + ssm + shiro 的權(quán)限框架
隨著前后端分離項目的熱潮,前端各大框架的,前后端溝通部分也成了問題,之前服務(wù)端渲染的頁面生成到前端來,現(xiàn)在前后端可能是兩個服務(wù)器,一些技術(shù)的遷移,本框架的權(quán)限部分的設(shè)計思想,借鑒了前端大牛的想法,也有傳統(tǒng)后端的設(shè)計方案,拋磚引玉,做個橋梁,實現(xiàn)前后端分離的權(quán)限的設(shè)計,代碼僅供參考,思路僅供參考,相信優(yōu)秀的你寫自己的代碼,用自己的思想會更為貼切,方便。 最終即具有統(tǒng)一響應(yīng)結(jié)構(gòu)、 前后臺數(shù)據(jù)流轉(zhuǎn)機制(HTTP消息與Java對象的互相轉(zhuǎn)化機制)、統(tǒng)一的異常處理機制、參數(shù)驗證機制、Cors跨域請求機制以及鑒權(quán)機制。
前端設(shè)計:采用 Vue 的 element ui ,對于前端設(shè)計者來說,應(yīng)該很好理解源碼。
后端設(shè)計:shiro + ssm + redis 存儲 jwt
交互方式:前端存儲jwt,在訪問后端時攜帶,這也是唯一交互驗證方式。
前期工作:設(shè)計符合需求的vue模板,路由,資源,角色,用戶其中對應(yīng)關(guān)系也可從數(shù)據(jù)表中體現(xiàn)出來。
設(shè)計思路
基本想法就是,用到Vuex 和 Vue Router 前者用來做狀態(tài)控制,后者綁定路由,這樣權(quán)限可以直接對應(yīng)到組件上,某個用于只能訪問某個組件,而不用將每個組件都加上權(quán)限控制,重要的是還有單點登錄。 所以拋磚引玉,寫一個通用框架,(至少是通用想法)具體可以模塊化來可插拔就ok 了。 非動態(tài)路由的問題是只能在拿到權(quán)限之后初始化Vue實例,因此必須把登陸頁從SPA中剝離出來做成一個獨立的頁面,用戶登錄/退出操作需要在兩個url之間跳轉(zhuǎn),體驗略差。
另一種做法是直接用所有路由實例化應(yīng)用,當(dāng)用戶登錄拿到權(quán)限后再通過元素操作隱藏越權(quán)菜單,這時用戶還可以手動輸入地址訪問越權(quán)頁面,因此還需要給路由加beforeEach鉤子來限制路由訪問,路由鉤子本身會增加一定的性能壓力,而且實例化即注冊所有路由也會使前端加載冗余的路由組件。 本系統(tǒng)采用的在初始路由注冊首頁和登錄頁,并在拿到token后得到權(quán)限,然后在實例化Vue實例。路由代碼如下:
const router = new Router({
routes: [
{
path: '/login',
name: "login",
component: LoginView,
meta: { requiresAuth: false }
},{
path: '/index',
redirect: '/',
meta: { requiresAuth: true }
}
]
});
generateIndexRouter();
// 驗證token,存在才跳轉(zhuǎn)
router.beforeEach((to, from, next) => {
let token = sessionStorage.getItem('token')
if(to.path === '/') {
if(!token) {
next({
path: '/login',
query: { redirect: to.fullPath }
})
return
}
}
if(to.meta.requiresAuth) {
if(token) {
next()
} else {
next({
path: '/login',
query: { redirect: to.fullPath }
})
}
} else {
next()
}
});
router.afterEach((to, from) => {
// 設(shè)置面包屑
let breadCrumbItems = []
let homePageCrumb = {
title: '首頁',
to: '/'
}
breadCrumbItems.push(homePageCrumb)
if(to.meta.nameFullPath) {
let fullPathSplit = to.meta.nameFullPath.split('/')
fullPathSplit.forEach(( item, index ) => {
let routerBreadCrumb = {
title: item,
to: (index == fullPathSplit.length - 1 ? to.path : '')
}
breadCrumbItems.push(routerBreadCrumb)
});
}
// 更新到state
router.app.$store.dispatch('setBreadcurmbItems', breadCrumbItems)
})
// 生成首頁路由
function generateIndexRouter() {
if (!sessionStorage.routers) {
return
}
let indexRouter = {
path: "/",
name: "/",
component: resolve => require(['@/views/home/index'], resolve),
children: [
...generateChildRouters()
]
}
router.addRoutes([indexRouter])
}
// 生成嵌套路由(子路由)
function generateChildRouters() {
let routers = JSON.parse(sessionStorage.routers)
let childRouters = []
for(let router of routers) {
if(router.code != null) {
let routerProps = JSON.parse(router.properties)
let childRouter = {
path: router.url,
name: router.code,
component: resolve => require(['@/views/' + router.code + '/index'], resolve),
meta: { routerId: router.id, requiresAuth: routerProps.meta.requiresAuth, nameFullPath: routerProps.nameFullPath }
}
childRouters.push(childRouter)
}
}
return childRouters
}
export default router;
前后端數(shù)據(jù)格式約定
既然是restful風(fēng)格,必然有通用返回狀態(tài)的類,遵循網(wǎng)上開源原則,一類繼承hashmap這樣達到可以返回任意的數(shù)據(jù),通用的數(shù)據(jù)就是code.msg.data這些,如果有分頁會另外加分頁,還有一種是,data.msg.state(code).token + 分頁類型的數(shù)據(jù)如:
"data": {
"list": null,
"pagebar": {
"page": 1,
"total": 2,
"limit": 10
}
},
"msg": "error",
"state": 0,
"is_redirect": true,
"redirect_url": "http://qq.com",
"token": null
本項目考慮到后期的擴展性,用到了第一類,其中實現(xiàn)了常用的失敗和成功的狀態(tài)碼及其響應(yīng),類名設(shè)計為Result,位于zhcc-common下面,一般性地是封裝到ResponseEntity中返回。
前后端數(shù)據(jù)接口約定
分別對應(yīng)http協(xié)議的get/put/post/delete方法,后端權(quán)限是:read/:update/:create/:delete
case "get": permissionString += ":read"; break; case "put": permissionString += ":update"; break; case "post": permissionString += ":create"; break; case "delete": permissionString += ":delete";
驗證部分
用的是com.baidu.unbiz.fluentvalidator.ValidationError 而不是hibernateValidator 減輕服務(wù)端編程等的壓力。直接在controller里面驗證,最后封裝到Result的fail方法里面返回。
權(quán)限的設(shè)計
權(quán)限的控制主要分為4大類,主要是基于RBAC原理。 路由,資源,角色,用戶 路由級別和組件級別可控制
過程設(shè)計
1.權(quán)限設(shè)計 2.異常設(shè)計 3.字典和其他接口設(shè)計 4.前后的通訊設(shè)計==
