0.1.2 • Published 2 years ago

sunny-rbac v0.1.2

Weekly downloads
-
License
MIT
Repository
-
Last release
2 years ago

sunny-rbac

npm version npm downloads license

页面和页面内操作的提供权限控制支持

Installation

使用npm安装

npm i sunny-rbac

使用yarn安装

yarn add sunny-rbac

使用pnpm安装

pnpm add sunny-rbac

Usages

集成用例

与可拔插路由模块(sunny-pluggable-router)集成

使用自定义路由组件 AuthRoute

import { implement as routerImplement } from 'sunny-pluggable-router'
import { AuthRoute } from 'sunny-auth'
import { Route } from 'react-router'
import hashHistory from '../hashHistory'

/**
 * 扩充 RouteConfig 类型增加auth属性
 */
declare module 'sunny-pluggable-router/dist/types/index.es' {
  interface RouteConfig {
    auth?: boolean
  }
}

/**
 * 为sunny-pluggable-router模块,使用自定义路由组件
 * 自定义选择路由组件的逻辑,可以为路由增加特别功能
 * 这里我们为路由配置增加了一个auth认证功能
 */
routerImplement({
  customRoute: route => {
    const isAdmin = true
    if (isAdmin) {
      // 适用于后台管理,后台管理项目默认启用认证路由
      // eslint-disable-next-line @typescript-eslint/no-unnecessary-boolean-literal-compare
      return route.auth === false ? Route : AuthRoute
    } else {
      // 适用于前端web应用
      return route.auth ? AuthRoute : Route
    }
  },
})

实现认证逻辑

// 实现基本的登录认证
import { implement as AuthImplement } from 'sunny-auth'

AuthImplement({
  authenticate: () => (isLogin() ? Promise.resolve() : Promise.reject()),
  requireLogin: () => {
    const pathname = routeConfig('login')?.path.toString()
    const from = hashHistory.location
    if (pathname && pathname !== from.pathname) {
      console.debug('forward to ', location, ' from ', from)
      hashHistory.push(pathname)
    }
  },
})

与授权路由模块集成

实现访问控制逻辑

import { implement as AuthImplement } from 'sunny-auth'
import {
  getPrivileges,
  getResources,
  hasResource,
  implement as RBACImplement,
} from 'sunny-rbac'
import { matchPath } from 'react-router'
import hashHistory from '../hashHistory'

RBACImplement({
  // 因用例代码使用了hash history而不是browser history,所以这里重写了模块获取location.pathname的默认逻辑
  currentResource: () => hashHistory.location.pathname,
})

AuthImplement({
  authorize: routeProps =>
    Promise.all([
      // 准备好RBAC权限控制模块所需的resources资源信息,后面的查询都会用到
      getResources()
        // 准备好RBAC权限控制模块当前resources所需的privileges信息
        .then(() => getPrivileges())
        .then(() => {
          /**
           * 使用matchPath是为了处理route.path为数组的情况
           */
          const match = matchPath(hashHistory.location.pathname, routeProps)
          /**
           * 查询当前菜单是否被授权访问
           * 如果/parent被允许,那么/parent/child也被允许
           */
          return (match && hasResource(match.path)) || Promise.reject()
        }),
    ]),
  deny: () => {
    console.debug(
      'The visitor is not allowed to go',
      hashHistory.location,
      '. Then go back.'
    )
  },
})

配置 sunny-rbac,对接后台权限系统接口。

import { implement as RBACImplement, clearCache } from 'sunny-rbac'

RBACImplement({
  // 向后端权限系统查询所有可用资源
  getResources: () => Promise.resolve([{ path: '/dashboard', id: 1 }]),
  // 向后端权限系统查询某个资源所有可用权限
  // 在运行时会用户的访问某些页面的行为而被调用
  getPrivileges: (/* { id: resourceId } */) => Promise.resolve([{ title: '操作名称' }]),
  // 查询用户是否为超级管理员,超管为上帝视角,拥有最高权力。所有权限查询都会通过
  isRootUser() {
    // roleId  === 1   超级管理员
    const roleId = 1
    return Number(roleId) === 1
  },
})

// 权限信息更新时,清空现有缓存,可以触发重新请求权限数据
// onPrivilegeUpdated(clearCache)
// onRequestLogin(clearCache)
// onSessionError(clearCache)
// onLogout(clearCache)

更多用例代码见 examplesstorybook 文档

How it works

介绍当前项目与后端 RBAC 模型权限系统对接在一起的协作方式。

术语表

术语别名翻译
一级权限页面/资源以页面为单位。体现为页面、页面路由、侧栏菜单
二级权限操作/权限以可用操作为单位。体现为页面中的单个按钮、一组交互组件、一个逻辑块

一级权限

整个页面

整个页面

页面路由

/sys/firstAuth

侧栏菜单

侧栏菜单

二级权限

单个按钮

单个按钮

一组交互组件

一组交互组件

可以将这一组交互组件命名为“搜索”,意思为只要有“搜索”权限,即可以使用 这一组组件。

一个逻辑块

import { hasPrivilege } from 'sunny-rbac'
if (hasPrivilege('合计')) {
  // do something else
}

了解协作方式

协作方式概述

先对路由访问进行控制,在对页面操作进行控制。 一个页面是否能访问,除了要检查登录态,还得检查路由对于当前用户来讲是否有权访问。 好在现在,可以利用自定义路由模块实现拦截、登录态查询、访问权限查询。

为了实现这些想法,设计了认证授权路由组件(sunny-auth), 权限控制模块(sunny-rbac)两个关键模块。 sunny-auth 用来路由拦截。sunny-rbac 用来提供对访问权限查询服务。

先了解一下基本工作流程。

页面访问控制流程

通用流程

访问路由 > 拦截访问 > 身份认证

正常流程

认证通过 > 允许访问

异常流程

认证失败 > 拒绝访问
页面操作控制流程

通用流程

操作权限 > 读取 > 查询

正常流程

有 > 通过 > 展示

异常流程

没有 > 拒绝 > 隐藏

在后台管理页面开发中的应用

新开发好的页面,通常已经定义好了路由信息,接下来就需要加入侧栏菜单,和为页面添加可用操作。

将新页面加入菜单

功能定位:权限路由 > 添加

将新页面加入菜单

为新页面添加可用操作权限

功能定位:权限路由 > 找到已添加的新页面 > 新增权限

为新页面添加可用操作权限

注意“权限路由”处填写接口地址

根据页面的功能设计,将剩余的“操作”依次加入

Browser Compatibility

浏览器兼容性

Changelog

See CHANGELOG.md