You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React-Admin usePermissions钩子授权后role未定义问题排查

权限获取异常问题排查

问题描述

我尝试通过usePermissions钩子获取角色来控制UI资源的访问权限,但授权完成后permissions.role的值始终是undefined,只有刷新浏览器页面后才能拿到预期值,请问问题出在哪?

相关代码

App组件代码

import * as React from "react";
// ... 其他导入
const store = localStorageStore();

function App() {
  return (
    <AdminContext
      authProvider={authProvider}
      dataProvider={CabDataProvider}
      i18nProvider={i18nProvider}
      store={store}
    >
      <AsyncResources />
    </AdminContext>
  );
}

interface CabDataProvider extends DataProvider {
  getResources: () => Promise<{ name: string }[]>;
}

function AsyncResources() {
  const [resources, setResources] = useState<Array<{ name: string }>>(
    [] as Array<{ name: string }>
  );
  const dataProvider = useDataProvider<CabDataProvider>();

  useEffect(() => {
    dataProvider.getResources().then((r) => setResources(r));
  }, []);

  const { isLoading, permissions } = usePermissions();

  if (isLoading == false) {
    console.log(permissions);
    return (
      <AdminUI ready={Loading} loginPage={CabLogin} dashboard={Dashboard}>
        {permissions.role === "admin" ? (
          <Resource
            name="users"
            create={UserCreate}
            list={UsersList}
            edit={UserEdit}
          />
        ) : null}
        <Resource name="offices" list={OfficesList} />
        <CustomRoutes noLayout>
          <Route path="/registration" element={<CabRegistration />} />
        </CustomRoutes>
      </AdminUI>
    );
  } else {
    return (
      <AdminUI ready={Loading} loginPage={CabLogin} dashboard={Dashboard}>
      </AdminUI>
    )
  }
}

export default App;

authProvider中的getPermissions方法

getPermissions: () => {
    try {
      const role = localStorage.getItem("role" as string)?.replace(/"/g, ""),
        permissions = localStorage.getItem("permissions" as string)?.replace(/"/g, "");
      return Promise.resolve({ role, permissions });
    } catch (error) {
      return Promise.reject({ message: "Failed to retrieve permissions", error });
    }
  }

原因分析

  1. 执行时机不匹配:usePermissions在组件挂载时就会调用authProvider.getPermissions(),但此时登录流程刚完成,localStorage里的role和permissions还没被写入,所以拿到的是undefined。
  2. 无监听机制:usePermissions默认不会监听localStorage的变化,所以登录后写入的数据无法触发权限的重新获取,只有刷新页面时,组件重新挂载才会读取到最新的localStorage值。

解决方案

1. 确保登录后正确写入localStorage

在authProvider的login方法中,必须在登录接口返回成功后,再将role和permissions写入localStorage,然后再resolve Promise,确保react-admin感知到登录完成:

login: async (credentials) => {
  // 调用登录接口获取用户信息
  const res = await yourLoginApi(credentials);
  // 写入localStorage(建议用JSON.stringify处理,避免格式问题)
  localStorage.setItem('role', JSON.stringify(res.role));
  localStorage.setItem('permissions', JSON.stringify(res.permissions));
  // 通知react-admin登录成功
  return Promise.resolve();
}

2. 让usePermissions感知数据变化

利用usePermissions返回的refetch方法,监听localStorage的变化,当role或permissions更新时,触发权限重新获取:

function AsyncResources() {
  const [resources, setResources] = useState<Array<{ name: string }>>([]);
  const dataProvider = useDataProvider<CabDataProvider>();
  // 解构出refetch方法
  const { isLoading, permissions, refetch } = usePermissions();

  useEffect(() => {
    dataProvider.getResources().then((r) => setResources(r));
  }, []);

  // 添加localStorage监听
  useEffect(() => {
    const handleStorageUpdate = (e: StorageEvent) => {
      if (e.key === 'role' || e.key === 'permissions') {
        refetch();
      }
    };
    window.addEventListener('storage', handleStorageUpdate);
    // 组件卸载时移除监听
    return () => window.removeEventListener('storage', handleStorageUpdate);
  }, [refetch]);

  if (!isLoading) {
    console.log(permissions);
    return (
      <AdminUI ready={Loading} loginPage={CabLogin} dashboard={Dashboard}>
        // 可选:添加可选链避免undefined报错
        {permissions?.role === "admin" && (
          <Resource
            name="users"
            create={UserCreate}
            list={UsersList}
            edit={UserEdit}
          />
        )}
        <Resource name="offices" list={OfficesList} />
        <CustomRoutes noLayout>
          <Route path="/registration" element={<CabRegistration />} />
        </CustomRoutes>
      </AdminUI>
    );
  } else {
    return (
      <AdminUI ready={Loading} loginPage={CabLogin} dashboard={Dashboard}>
      </AdminUI>
    )
  }
}

3. 优化getPermissions的取值逻辑

用JSON.parse代替字符串替换,避免格式错误导致的取值异常:

getPermissions: () => {
  try {
    const roleStr = localStorage.getItem("role");
    const permissionsStr = localStorage.getItem("permissions");
    // 解析JSON字符串,避免引号替换的脆弱性
    const role = roleStr ? JSON.parse(roleStr) : null;
    const permissions = permissionsStr ? JSON.parse(permissionsStr) : null;
    return Promise.resolve({ role, permissions });
  } catch (error) {
    return Promise.reject({ message: "Failed to retrieve permissions", error });
  }
}

内容的提问来源于stack exchange,提问作者1vanguard

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.10 04:30:58