網(wǎng)站首頁 編程語言 正文
前話
具備用戶體系的業(yè)務(wù)系統(tǒng)往往需要具備權(quán)限控制的能力。前后不分離的開發(fā)模式權(quán)限控制主要由后端結(jié)合模版引擎實現(xiàn),而前后分離的開發(fā)模式由前后兩端協(xié)商權(quán)限配置分別進行數(shù)據(jù)權(quán)限控制和組件權(quán)限控制。
正文
權(quán)限配置格式不限,為進行后續(xù)演示本篇設(shè)定權(quán)限配置如下:
export type IAuthConfig = { ? /** 權(quán)限標(biāo)識列表 */ ? keys: string[]; };
且提供一個Hook直接獲取當(dāng)前用戶信息:
export type IUser = { ? /** 權(quán)限配置 */ ? authConfig: IAuthConfig; }; /** 用戶信息Hook */ export function useUser() { ? // ...略 ? return user; }
首先我們先定義一個權(quán)限Hook,內(nèi)容如下:
/** ?* 轉(zhuǎn)換為權(quán)限標(biāo)識數(shù)組 ?* @param auth 權(quán)限標(biāo)識 ?* @returns ?*/ function getAuthKeys(auth: string | string[]) { ? return Array.isArray(auth) ? auth : [auth]; } /** 權(quán)限鑒權(quán)類型 */ export enum EAuthType { ? /** 或權(quán)限(至少匹配一個) */ ? "some" = "some", ? /** 與權(quán)限(全部匹配) */ ? "every" = "every", } /** 權(quán)限Hook */ export function useAuth() { ? const { authConfig } = useUser(); ? // 權(quán)限標(biāo)識列表 ? const authKeys = useMemo(() => authConfig?.keys ?? [], [authConfig]); ? // 校驗是否具備權(quán)限 ? const hasAuth = useCallback( ? ? (auth: string | string[], type?: EAuthType) => ? ? ? getAuthKeys(auth)[type ?? EAuthType.every]((key) => ? ? ? ? authKeys.includes(key) ? ? ? ), ? ? [authKeys] ? ); ? const ret: [typeof authKeys, typeof hasAuth] = [authKeys, hasAuth]; ? return ret; }
1. 控制方式
對于前端開發(fā)而言一般只需做組件控制,控制方式有很多種寫法。
1.1 直接計算
const Cmpt: React.FC = () => { ? const [, hasAuth] = useAuth(); ? // 計算是否有權(quán)限 ? const authorized = useMemo(() => hasAuth("test"), [hasAuth]); ? // ...略 };
1.2 通用權(quán)限Hoc
export function withAuth<P extends Record<string, unknown> = {}>( ? auth: string | string[], ? type?: EAuthType ) { ? return function (Component: any) { ? ? const WrappedComponent: React.FC<P> = (props) => { ? ? ? const [, hasAuth] = useAuth(); ? ? ? const instance = React.createElement(Component, { ? ? ? ? ...props, ? ? ? }); ? ? ? // 計算是否有權(quán)限 ? ? ? const authorized = hasAuth(auth, type); ? ? ? // ...略 ? ? }; ? ? WrappedComponent.displayName = `withAuth(${getDisplayName(Component)})`; ? ? return WrappedComponent; ? }; }
1.3 權(quán)限包裹組件
const AuthWrapper: React.FC<IProps> = ({ auth, type, children }) => { ? const [, hasAuth] = useAuth(); ? // 計算是否有權(quán)限 ? const authorized = useMemo(() => hasAuth(auth, type), [auth, type, hasAuth]); ? // ...略 };
2. 控制結(jié)果
常見控制結(jié)果為控制組件的顯示或隱藏,或根據(jù)是否具備權(quán)限做組件的自定義渲染。
為方便演示后面統(tǒng)一使用權(quán)限包裹組件進行說明。
2.1 顯隱控制
具備權(quán)限的組件直接渲染,否則返回null,即可實現(xiàn)顯隱控制。權(quán)限包裹組件實現(xiàn)如下:
const AuthWrapper: React.FC<IProps> = ({ auth, type, children }) => { ? const [, hasAuth] = useAuth(); ? // 計算是否有權(quán)限 ? const authorized = useMemo(() => hasAuth(auth, type), [auth, type, hasAuth]); ? // 具備權(quán)限的組件直接渲染,否則返回null ? return authorized ? children : null; };
在需要權(quán)限控制的組件外包裹權(quán)限組件即可。
const Cmpt: React.FC = () => { ? <> ? ? <AuthWrapper auth="test"> ? ? ? <Button>Hello World</Button> ? ? </AuthWrapper> ? </>; };
2.2 自定義渲染
實際業(yè)務(wù)場景中,存在需要提醒用戶沒有權(quán)限的情況,這時需要權(quán)限包裹組件支持自定義渲染,實現(xiàn)方式有多種:
- 靜態(tài)props注入
- 動態(tài)props映射
- render props
相比較之下render props更為自由,權(quán)限包裹組件完善后實現(xiàn)如下:
type IProps = { ? /** 權(quán)限標(biāo)識 */ ? auth: string | string[]; ? /** 鑒權(quán)類型 */ ? type?: EAuthType; ? /** 子內(nèi)容 */ }; const AuthWrapper: React.FC<IProps> = ({ auth, type, children }) => { ? const [, hasAuth] = useAuth(); ? // 計算是否有權(quán)限 ? const authorized = useMemo(() => hasAuth(auth, type), [auth, type, hasAuth]); ? // 自定義渲染 ? if (typeof children === "function") { ? ? return children(authorized); ? } ? // 顯隱控制 ? return authorized ? children : null; };
這時就可以渲染提示無權(quán)限組件:
const Cmpt: React.FC = () => { ? <> ? ? <AuthWrapper auth="test"> ? ? ? {(authorized) => <Button disabled={!authorized}>Hello World</Button>} ? ? </AuthWrapper> ? </>; };
3. 權(quán)限數(shù)據(jù)
前端開發(fā)做組件控制的顆粒度取決于權(quán)限數(shù)據(jù)的類型,權(quán)限數(shù)據(jù)類型分為靜態(tài)權(quán)限和動態(tài)權(quán)限。
其中靜態(tài)權(quán)限一般在完成登錄認證后即可一次性獲取,而動態(tài)權(quán)限則在操作數(shù)據(jù)時進行權(quán)限校驗。
因此不難發(fā)現(xiàn)靜態(tài)權(quán)限往往用于控制路由、頁面或者粗糙的操作權(quán)限。而動態(tài)權(quán)限則能夠?qū)討B(tài)數(shù)據(jù)進行更細粒度的操作權(quán)限控制(需后端數(shù)據(jù)權(quán)限控制能力配合)。
3.1 靜態(tài)權(quán)限
如前面描述,登錄認證后即可從用戶信息中得到權(quán)限標(biāo)識,同樣前面的栗子均也為靜態(tài)權(quán)限示例。
3.2 動態(tài)權(quán)限
需要動態(tài)權(quán)限校驗的場景在業(yè)務(wù)系統(tǒng)中也較為常見,例如用戶能夠看到列表數(shù)據(jù),但禁止查閱無權(quán)限的數(shù)據(jù)詳情。
由此可知對于用戶而言,獲取的動態(tài)的列表數(shù)據(jù)需要逐一進行權(quán)限校驗,這時我們對權(quán)限Hook和包裹組件進行改造,改造后代碼如下:
type IAuthDynamicConfig = { ? // ...略 }; /** 權(quán)限Hook */ export function useAuth() { ? // ...略 ? // 動態(tài)校驗是否具有權(quán)限 ? const dynamicAuth = useCallback( ? ? (auth: string | string[], type?: EAuthType, config?: IAuthDynamicConfig) => ? ? ? // 批量異步校驗權(quán)限,實現(xiàn)略 ? ? ? append2DynamicAuthTask(auth, type, config), ? ? [] ? ); ? const ret: [typeof authKeys, typeof hasAuth, typeof dynamicAuth] = [ ? ? authKeys, ? ? hasAuth, ? ? dynamicAuth, ? ]; ? return ret; } /** 權(quán)限包裹組件 */ const AuthWrapper: React.FC<IProps> = ({ auth, type, config, children }) => { ? const [, hasAuth, dynamicAuth] = useAuth(); ? const [authorized, setAuthorized] = useState(false); ? // 計算是否有權(quán)限 ? useEffect(() => { ? ? if (config === undefined) { ? ? ? setAuthorized(hasAuth(auth, type)); ? ? ? return; ? ? } ? ? dynamicAuth(auth, type, config).then(setAuthorized); ? }, [auth, type, config, hasAuth, dynamicAuth]); ? // ...略 };
使用方式如下:
const Cmpt: React.FC = () => { ? // ...略 ? <> ? ? {data.map((item) => ( ? ? ? <div key={item.id}> ? ? ? ? <div>{item.title}</div> ? ? ? ? <div> ? ? ? ? ? <AuthWrapper ? ? ? ? ? ? auth="detail" ? ? ? ? ? ? config={{ ? ? ? ? ? ? ? module: "demo", ? ? ? ? ? ? ? identify: item.id, ? ? ? ? ? ? }} ? ? ? ? ? > ? ? ? ? ? ? {(authorized) => ( ? ? ? ? ? ? ? <Button disabled={!authorized} onClick={handleDetail}> ? ? ? ? ? ? ? ? 詳情 ? ? ? ? ? ? ? </Button> ? ? ? ? ? ? )} ? ? ? ? ? </AuthWrapper> ? ? ? ? </div> ? ? ? </div> ? ? ))} ? </>; };
原文鏈接:https://juejin.cn/post/7064124989699522590
相關(guān)推薦
- 2022-05-17 獲取當(dāng)前機器注冊到nacos上的機器ip
- 2022-11-08 Python?運算符Inplace?與Standard?_python
- 2022-04-03 C#中的char與string詳解_C#教程
- 2022-07-15 Android自定義view繪制表格的方法_Android
- 2022-11-08 uni-app在手機上打開背景圖片不顯示
- 2023-07-31 Failed to execute ‘toDataURL‘ on ‘HTMLCanvasElemen
- 2022-05-29 Android超詳細講解彈出多選框的實現(xiàn)_Android
- 2022-10-21 C++?ffmpeg硬件解碼的實現(xiàn)方法_C 語言
- 最近更新
-
- window11 系統(tǒng)安裝 yarn
- 超詳細win安裝深度學(xué)習(xí)環(huán)境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎(chǔ)操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區(qū)別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權(quán)
- redisson分布式鎖中waittime的設(shè)
- maven:解決release錯誤:Artif
- restTemplate使用總結(jié)
- Spring Security之安全異常處理
- MybatisPlus優(yōu)雅實現(xiàn)加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務(wù)發(fā)現(xiàn)-Nac
- Spring Security之基于HttpR
- Redis 底層數(shù)據(jù)結(jié)構(gòu)-簡單動態(tài)字符串(SD
- arthas操作spring被代理目標(biāo)對象命令
- Spring中的單例模式應(yīng)用詳解
- 聊聊消息隊列,發(fā)送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支