版本变更请查看 CHANGELOG
一个面向 Taro + React 的轻量权限库,提供:
- 资源-动作权限模型(Record<string, string[]>),支持通配符“*”与正则资源匹配
- 路由守卫与导航代理(navigateTo/redirectTo/switchTab),自动拦截无权限访问
- 初始化前 API 调用排队,避免“白屏/误伤渲染”与竞态问题
- React 生态集成:PermissionWrapper 组件与 useRoutePermission Hook
- 简单类型与事件机制,权限更新后自动刷新视图
本库依赖以下 peerDependencies:
- @tarojs/taro >= 3
- react >= 17
使用你熟悉的包管理器安装:
# pnpm
pnpm add poter @tarojs/taro react
# npm
npm i poter @tarojs/taro react
# yarn
yarn add poter @tarojs/taro react导入:
import Poter, { PermissionWrapper, useRoutePermission, type PoterRoute, type PoterGrantedPermission } from "poter"import Poter, { type PoterRoute, type PoterGrantedPermission } from "poter"
// 路由权限需求(可选 oneOfPerm:任一满足即通过;默认需要全部满足)
const routes: PoterRoute[] = [
{ url: "/pages/article/index", requiredPermissions: [{ resource: "article", actions: ["read"] }] },
{ url: "/pages/sys/index", requiredPermissions: [{ resource: /^sys:.+$/, actions: ["manage"] }], oneOfPerm: true },
]
// 当前用户已授予权限:资源 -> 动作
// 支持通配符:例如 ["*"] 代表该资源下的所有动作
const userPermissions: PoterGrantedPermission = {
article: ["read"],
"sys:role": ["manage"],
}
Poter.init(routes, userPermissions)// 根据 url 判断是否可访问(若路由未配置权限,默认放行)
const canVisit = Poter.authentication("/pages/article/index")
// 自定义校验:传入所需权限数组
const allowed = Poter.check({
requiredPermissions: [
{ resource: "article", actions: ["read"] },
{ resource: /^sys:.+$/, actions: ["manage"] },
],
oneOfPerm: true, // 任一满足即通过
})try {
await Poter.navigateTo({ url: "/pages/article/index" })
} catch (e) {
// 无权限时抛出 { code: 401, message: "权限验证失败" }
}
// redirectTo / switchTab 同理;navigateBack 不做权限限制并立即执行import { useRoutePermission, PermissionWrapper } from "poter"
// 路由权限鉴权 Hook(返回 canAccess/loading/error/refresh)
const { canAccess, loading, error, refresh } = useRoutePermission("/pages/article/index")
// 组件:基于权限包裹 UI
<PermissionWrapper
requiredPermissions={[{ resource: "article", actions: ["read"] }]}
backup={<span>无权限</span>}
>
<YourComponent />
</PermissionWrapper>-
PoterGrantedPermission:Record<资源, 动作[]>,例如:
const perms = { article: ["read", "write"], "sys:role": ["manage"], product: ["*"], // 通配符:任意动作均可 }
-
PoterAuth.resource 支持 string 或 RegExp:
- string:直接从用户权限中读取该 key
- RegExp:对所有 key 做匹配,必须全部匹配项都满足 actions 要求
-
actions 判断规则:
- 若对应资源的权限数组 join("") === ""(如 [""]),视为对该资源下所有动作放行
- 否则要求 actions 中的每个动作均包含在权限数组中
-
路由未配置 requiredPermissions 时,默认放行
-
init(routes: PoterRoute[], userPermissions: PoterGrantedPermission): void
- 构造内部实例并触发事件通知(组件/Hook 会自动刷新)
- 初始化完成后会自动刷新排队中的调用
-
updateUserPermission(userPermissions: PoterGrantedPermission): void
- 更新当前用户权限并触发刷新
-
authentication(url: string): boolean
- 根据预设 routes 判断是否可访问
- 未初始化时,安全默认值为 true(避免误伤渲染)
-
authRoute(url: string, options?: { waitInit?: boolean; defaultValue?: boolean }): boolean | Promise
- waitInit = false(默认):未初始化时直接返回 defaultValue(默认 false,不入队)
- waitInit = true:若未初始化则入队等待,最终返回真实鉴权结果(始终 Promise)
-
check(params: PoterAuthParams): boolean
- 自定义校验:传 requiredPermissions 与 oneOfPerm
-
navigateTo(options: Taro.navigateTo.Option): Promise
-
redirectTo(options: Taro.redirectTo.Option): Promise
-
switchTab(options: Taro.switchTab.Option): Promise
- 导航前会进行权限校验,失败抛出 { code: 401, message: "权限验证失败" }
-
navigateBack(options?: Taro.navigateBack.Option): Promise
- 不做权限限制,立即执行
队列语义:在 init 之前调用的鉴权/导航,会被排队等待初始化完成后串行执行,避免竞态问题。
针对路由 url 的异步权限鉴权 Hook,内部监听权限初始化与更新事件自动刷新。
源码签名:useRoutePermission(url: string, options?: { immediate?: boolean; defaultValue?: boolean }, deps: ReadonlyArray<unknown> = [])
注意:依赖数组是第三个独立参数,不在 options 内。
interface UseRoutePermissionOptions {
immediate?: boolean // 默认 true,挂载后立即鉴权
defaultValue?: boolean // 默认 false(初始 canAccess)
}
// 基础用法(立即鉴权)
const { canAccess, loading } = useRoutePermission("/pages/article/index")
// 自定义默认值 & 禁用挂载立即鉴权
const p = useRoutePermission("/pages/article/index", { immediate: false, defaultValue: true })
// 带额外依赖(依赖变化会重新触发 refresh)
const { canAccess, refresh } = useRoutePermission(dynamicUrl, { immediate: true }, [dynamicUrl, userId])返回字段:
- canAccess: boolean 当前是否允许访问
- loading: boolean 当前是否在执行鉴权
- error: unknown 鉴权异常(通常不抛,但保留)
- refresh: () => Promise 手动重新鉴权
type PermissionWrapperProps = {
requiredPermissions?: Array<{ resource: string | RegExp; actions?: string[] }>
oneOfPerm?: boolean
backup?: React.ReactNode // 无权限时的兜底渲染
}- 根据 requiredPermissions/oneOfPerm 判定是否渲染 children,否则渲染 backup(或 null)
- 内部会在权限初始化/变更后自动刷新
- PoterGrantedPermission = Record<string, string[]>
- PoterAuth = { resource: string | RegExp; actions?: string[] }
- PoterAuthParams = { requiredPermissions?: PoterAuth[]; oneOfPerm?: boolean }
- PoterRoute = { url: string; requiredPermissions?: PoterAuth[]; oneOfPerm?: boolean }
库内部使用 mitt 维护两个事件:
- "toter:init" – 初始化完成时派发
- "toter:updateUserPermission" – 用户权限更新时派发
Hook 与组件已内置订阅这两个事件,无需在业务侧直接使用。
要求 Node >= 20.19。构建使用 Vite,输出 ESM 与 CJS:
- dist/poter.mjs
- dist/poter.cjs
常用脚本:
pnpm install # 安装依赖
pnpm run test # 运行单元测试(vitest)
pnpm run build # 构建库(产物位于 dist/)- 未初始化行为
- authentication 返回 true(安全默认值,用于同步快速判断)
- authRoute(url,{waitInit:false}) 直接返回 defaultValue(默认 false,不触发排队)
- authRoute(url,{waitInit:true}) / 导航 API 会入队等待 init 完成后再执行并返回真实结果
- 导航异常
- 无权限时抛出 { code: 401, message: "权限验证失败" }
- 正则资源
- 会匹配到的所有资源都需满足 actions 判定
- 通配符权限
- 将资源权限设为 ["*"],代表对该资源下的任意动作放行
MIT © recvexi