当前项目的角色
enum Role {
owner = 1,
member,
admin,
account,
}
预计实现效果,通过可配置项对前端路由自动过滤。
export const permission: Record<Role, PayoutPermission> = {
[Role.owner]: {
entry: true,
},
[Role.admin]: {
entry: false,
},
[Role.member]: {
entry: false,
},
[Role.account]: {
entry: true,
},
}
实现步骤
- 在需要增加rbac的Route中增加对应字段
const DashboardRouteConfig: RouteProps = {
path: '/dashboard',
wrapper: PrivateAuthWrapper,
component: PrivateLayout,
children: [
{
path: '',
exact: true,
component: UsersHome,
rbac: 'user.entry',
navItem: {
label: 'Home',
icon: 'Home',
index: 1,
},
},
]
}
- 在src下创建rbac文件夹,为需要rbac的route增加对应的permission
export const permission: Record<Role, PayoutPermission> = {
[Role.owner]: {
entry: true,
},
[Role.admin]: {
entry: false,
},
[Role.member]: {
entry: false,
},
[Role.account]: {
entry: true,
},
}
- rbac暴露统一的getPermissions(role)方法,getPermissions根据角色返回对应route的permission
export const getPermissions = (role: Role) => {
const debitCard = debitCardPermission[role]
const boosterCard = boosterCardPermission[role]
const standardCard = standardCardPermission[role]
const transaction = transactionPermission[role]
const team = teamPermission[role]
const setting = settingPermission[role]
const wallet = WalletPermission[role]
const payout = PayoutPermission[role]
const invoice = InvoicePermission[role]
const user = UserPermission[role]
const tally = TallyPermission[role]
const rewards = RewardsPermission[role]
const payroll = PayrollPermission[role]
const ledgerRole = LedegerRolePermission[role]
return {
debitCard,
boosterCard,
standardCard,
transaction,
team,
setting,
rewards,
wallet,
payout,
invoice,
user,
tally,
payroll,
ledgerRole,
}
}
export type Rbac = ReturnType<typeof getPermissions>
- 对当前route tree进行递归遍历,把如果route中无rbac字段直接返回,有rbac字段,进行权限校验
const routeFilter = (
route: RouteProps,
testPermission: ReturnType<typeof permissionsVerify>,
rootState?: Partial<RootState>
) => {
if (route.devRoute && process.env.NODE_ENV !== 'development') {
return null
}
if (route.filter && rootState && !route.filter(rootState)) {
return null
}
if (route.rbac && !testPermission(route.rbac)) {
return null
}
if (route.children) {
const filteredChildren = []
for (const childRoute of route.children) {
const validRoute = routeFilter(childRoute, testPermission, rootState)
if (validRoute) {
filteredChildren.push(validRoute)
}
}
if (filteredChildren.length === 0) {
return null
}
route = {
...route,
children: filteredChildren,
}
}
return route
}
- testPermission根据rbac进行权限校验
export const permissionsVerify =
(role: Role, isAccountant?: boolean) => (rules: string | string[]) => {
if (isAccountant && role === 3) {
role = 4
}
const rolePermissionTree = getPermissions(role)
rules = Array.isArray(rules) ? rules : [rules]
return rules.every((rule) => !!getPath(rolePermissionTree, rule))
}
export function getPath(obj: any, path: string) {
const normalizedPath = normalizeKeypath(path)
if (normalizedPath.indexOf('.') < 0) {
return obj[path]
}
const pathList = normalizedPath.split('.')
let d = -1
const l = pathList.length
while (++d < l && obj != null) {
obj = obj[pathList[d]]
}
return obj
}
当我们拿到过滤完成后的路由时,就可以对router进行render
function RoutesRender({ routes }: { routes: RouteProps[] }) {
const { path } = useRouteMatch()
return (
<Switch>
{routes.map(
(
{
path: relativePath,
children,
component: RenderComponent,
wrapper: Wrapper,
...routeProps
},
index
) => {
const basePath = urlJoin(path)
const pathname = basePath(relativePath || '')
const withWrapperRender = withWrapperComponent(Wrapper)
if (!children) {
// avoid members to go to the default route '/dashboard' which is a blank page in this routes
return (
<Route
{...routeProps}
path={pathname}
key={(relativePath || '') + index}
render={() => withWrapperRender(<RenderComponent />)}
/>
)
}
return (
<Route path={pathname} exact={routeProps.exact} key={relativePath}>
{withWrapperRender(
<RenderComponent>
<RoutesRender routes={children} />
</RenderComponent>
)}
</Route>
)
}
)}
</Switch>
)
}
Top comments (0)