ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

react-admin Auth Provider 编写指南:从登录鉴权到 Access Control 的完整实战

react-admin Auth Provider 编写指南:从登录鉴权到 Access Control 的完整实战 react-admin Auth Provider 编写指南从登录鉴权到 Access Control 的完整实战【免费下载链接】react-adminA frontend Framework for single-page applications on top of REST/GraphQL APIs, using TypeScript, React and Material Design项目地址: https://gitcode.com/gh_mirrors/re/react-adminreact-admin 作为构建在 REST/GraphQL API 之上的前端框架本身不绑定任何认证后端而是通过一个轻量的适配器对象——authProvider——来完成登录、会话校验、错误处理、身份获取与权限控制等全部认证/授权工作。本篇指南以 AuthProviderWriting.md 为主体骨架结合 ra-core 源码 中AuthProvider类型定义与useLogin、useLogout、useCheckAuth、useHandleAuthCallback、useCanAccess等钩子的真实实现带你逐个掌握authProvider的 7 个方法调用时机、请求/响应/错误契约、典型实现代码以及如何用 TypeScript 在编译期保证其正确性。读完你不仅能写出一个可运行的完整authProvider还能理解其背后 react-admin 的调用链与跳转逻辑从而应对登录态过期、第三方 OAuth、基于角色的细粒度授权等真实场景。理解 authProvider认证后端的适配器react-admin 不关心你的认证后端是自建的登录接口、Auth0/Cognito 等 OAuth 服务还是简单的本地校验——它只要求你提供一个适配器对象即authProvider。这个对象由一组方法组成react-admin 在合适的时机调用它们来处理认证authentication与授权authorizationconst authProvider { // REQUIRED必选 // 向认证服务器发送用户名和密码取回凭据 // 用于 login / password 流程 async login(params) {/* ... */}, // 当 dataProvider 返回错误时检查它是否是认证错误 async checkError(error) {/* ... */}, // 当用户导航时确认其凭据仍然有效 async checkAuth(params) {/* ... */}, // 清除本地凭据并通知认证服务器用户已登出 async logout() {/* ... */}, // OPTIONAL可选 // 获取用户资料id、fullName、avatar async getIdentity() {/* ... */}, // 处理第三方认证服务如 Auth0的回调 async handleCallback() {/* ... */}, // 检查用户是否可以对某个资源执行某个操作 // 用于 Access Control 式授权 async canAccess(params) {/* ... */}, // 获取当前用户的权限对象 // 用于 Permissions 式授权 async getPermissions() {/* ... */}, };从仓库源码看AuthProvider 类型定义 精确描述了这些方法的签名login返回{ redirectTo?: string | boolean } | void | anylogout返回void | false | stringcanAccess接收{ action, resource, record? }并返回Promiseboolean。如果使用 TypeScript可以直接借助AuthProvider类型在编译期校验实现是否正确import type { AuthProvider } from react-admin; const authProvider: AuthProvider { // ... };历史兼容react-admin 早期版本要求authProvider是(type, params) Promise的函数形式。当前仓库仍保留 convertLegacyAuthProvider.ts 用于将旧式函数自动转换为对象形式通过AUTH_LOGIN、AUTH_LOGOUT、AUTH_CHECK、AUTH_ERROR、AUTH_GET_PERMISSIONS等常量分发调用因此旧代码无需重写即可平滑迁移。一个最小可运行的示例下面是一个虚构但可运行的实现只接受用户john搭配密码123并将登录状态保存在localStorage中。const authProvider { async login({ username, password }) { if (username ! john || password ! 123) { throw new Error(Login failed); } localStorage.setItem(username, username); }, async checkError(error) { const status error.status; if (status 401 || status 403) { localStorage.removeItem(username); throw new Error(Session expired); } // 其他错误码404、500 等无需登出 }, async checkAuth() { if (!localStorage.getItem(username)) { throw new Error(Not authenticated); } }, async logout() { localStorage.removeItem(username); }, async getIdentity() { const username localStorage.getItem(username); return { id: username, fullName: username }; }, };这个最小实现覆盖了 4 个必选方法 1 个可选方法可以作为任何自定义认证后端的起点模板。login处理登录凭据项目说明用途向认证服务器发送用户名和密码并取回凭据必选是使用场景login / password 流程成功时重定向到之前的页面或 admin 首页可自定义失败时在通知中展示错误信息请求格式包含登录表单各字段值的Object响应格式void \| { redirectTo?: string \| boolean }错误格式string \| { message?: string }只要 admin 配置了authProviderreact-admin 就会在/login路由上启用一个内置登录表单页面见 登录表单示意。提交表单时react-admin 调用authProvider.login()并把登录数据作为参数传入方法正常 resolve 表示登录成功抛出错误则表示登录失败。以下示例展示如何通过 HTTPS 调用认证接口并把返回的凭据token存入localStorage// in src/authProvider.js const authProvider { async login({ username, password }) { const request new Request(https://mydomain.com/authenticate, { method: POST, body: JSON.stringify({ username, password }), headers: new Headers({ Content-Type: application/json }), }); let response; try { response await fetch(request); } catch (_error) { throw new Error(Network error); } if (response.status 200 || response.status 300) { throw new Error(response.statusText); } const auth await response.json(); localStorage.setItem(auth, JSON.stringify(auth)); }, // ... };登录成功后的跳转login()resolve 后react-admin 默认重定向到用户之前访问的页面如果用户是直接访问登录页则跳转到 admin 首页。查看 useLogin.ts 的实现可以发现重定向目标是「location state 中记录的nextPathnamenextSearch」否则是defaultAuthParams.afterLoginUrl默认为/见 useAuthProvider.ts同时该钩子会调用queryClient.invalidateQueries({ queryKey: [auth, getPermissions] })清空缓存的权限查询确保重新登录后权限立即刷新。登录失败login()抛出的 Error 会被 react-admin 通过通知组件展示给用户。自定义重定向login()若返回带redirectTo路径的对象登录后将跳转到该路径返回{ redirectTo: false }则完全禁用跳转// in src/authProvider.js const authProvider { async login({ username, password }) { // ... return { redirectTo: false }; }, // ... };安全提醒像示例这样把凭据存进localStorage可以避免页面刷新或切换浏览器标签后要求用户重新登录但也意味着应用对 XSS 攻击更敏感。建议在服务端同时下发httpOnlyCookie双管齐下加固安全。checkError识别认证错误项目说明用途判断 dataProvider 返回的错误是否为认证错误必选是使用场景总是成功时-失败时登出用户并重定向到登录页可自定义请求格式{ message: string, status: number, body: Object }来自 dataProvider 的错误响应格式void错误格式{ message?: string \| boolean, redirectTo?: string \| boolean, logoutUser?: boolean }当用户凭据缺失或失效时安全的 API 通常会返回 HTTP 401 或 403。幸运的是每次dataProvider返回错误react-admin 都会调用authProvider.checkError()来判定这是否属于认证错误如果该方法自身抛错react-admin 会立即调用authProvider.logout()并把用户重定向到登录页。因此由你决定哪些状态码应该放行resolve、哪些应该登出reject。例如对 401 和 403 都登出const authProvider { async checkError(error) { const status error.status; if (status 401 || status 403) { localStorage.removeItem(auth); throw new Error(); } // 其他错误码404、500 等无需登出 }, // ... };自定义重定向checkError()抛错时react-admin 默认重定向到/login若错误对象带redirectTo属性则跳转到该地址const authProvider { async checkError(error) { const status error.status; if (status 401 || status 403) { localStorage.removeItem(auth); const error new Error(); error.redirectTo /credentials-required; throw error; } }, // ... };不登出、仅跳转可通过error.logoutUser false配合error.redirectTo实现——保留登录状态只是把用户引导到指定页面const authProvider { async checkError(error) { const status error.status; if (status 401 || status 403) { localStorage.removeItem(auth); const error new Error(); error.redirectTo /credentials-required; error.logoutUser false; throw error; } }, // ... };控制错误通知checkError()抛错时 react-admin 默认会向用户展示通知把error.message设为false即可禁用const authProvider { async checkError(error) { const status error.status; if (status 401 || status 403) { localStorage.removeItem(auth); const error new Error(); error.message false; throw error; } }, // ... };checkAuth路由级会话校验项目说明用途判断用户是否已认证导航到受保护路由时必选是使用场景总是成功时-失败时登出用户并重定向到登录页可自定义请求格式传给useCheckAuth()的参数——react-admin 默认路由为空响应格式void错误格式{ message?: string \| boolean, redirectTo?: string \| boolean }仅仅依赖 REST 响应的 401 状态码通常不够react-admin 在客户端保留了数据即使凭据已经失效也可能在等待服务器响应的间隙短暂展示陈旧数据。因此每当用户导航到 list、edit、create、show 页面时react-admin 都会调用authProvider.checkAuth()。若该方法抛错react-admin 会调用authProvider.logout()并重定向到登录页——这是确认凭据仍然有效的最佳位置。例如检查localStorage中是否存在认证数据const authProvider { async checkAuth() { if (!localStorage.getItem(auth)) { throw new Error(); } }, // ... };自定义重定向checkAuth()抛错时默认重定向到/login可以通过错误对象的redirectTo属性覆盖const authProvider { async checkAuth() { if (!localStorage.getItem(auth)) { const error new Error(); error.redirectTo /no-access; throw error; } }, // ... }优先级提示如果authProvider.checkAuth()与authProvider.logout()都返回了重定向 URLcheckAuth()的返回值优先。自定义通知消息抛出的错误消息会被传给翻译层渲染因此可以直接使用翻译 key如login.requiredconst authProvider { async checkAuth() { if (!localStorage.getItem(auth)) { throw new Error(login.required); // react-admin 会把错误消息交给翻译层处理 } }, // ... };同样地把错误对象的message设为false可完全禁用通知const authProvider { async checkAuth() { if (!localStorage.getItem(auth)) { const error new Error(); error.message false; throw error; } }, // ... };从 useCheckAuth.ts 的实现可以看到完整的调用链捕获到checkAuth抛出的错误后先以error.redirectTo ?? /login作为目标调用logout()再根据error.message false决定是否notify(getErrorMessage(error, ra.auth.auth_check_error), { type: error })——默认文案是内置翻译的ra.auth.auth_check_error。匿名访问被标记为允许匿名访问的路由见 Authentication.md 中的匿名访问说明不会触发checkAuth调用。logout清理凭据并登出项目说明用途在后端登出用户并清理认证数据必选是使用场景总是成功时重定向到登录页可自定义失败时-请求格式-响应格式string \| false \| void登出后的重定向路由默认为/login错误格式-启用认证后react-admin 会在顶栏的用户菜单移动端为滑出菜单中添加登出按钮。用户点击后调用authProvider.logout()并清除 react-admin Store 中可能存储的敏感数据随后重定向到登录页。前面两个小节也展示了 react-admin 会在 API 返回 403 或本地凭据过期时主动调用logout()。authProvider.logout()负责清理当前认证数据。例如移除localStorage中的 tokenconst authProvider { async logout() { localStorage.removeItem(auth); }, // ... };它同时也是通知认证后端「该凭据已失效」的好地方。自定义重定向登出后react-admin 重定向到logout()返回的字符串路径返回false则禁用跳转const authProvider { async logout() { localStorage.removeItem(auth); return /my-custom-login; }, // ... };在 useLogout.ts 的实现中还有几个值得注意的细节最终重定向目标为redirectFromCaller || redirectFromLogout || loginUrlloginUrl默认/login优先级是「调用方传入 方法返回值 默认登录页」若返回的是以http开头的绝对地址如https://my.oidc.server/login会通过window.location.href整页跳转适用于对接 OIDC 等外部登录服务跳转与 Store 重置通过setTimeout(..., 0)延迟执行以避免与usePermissions查询重置之间产生竞态导致LogoutOnMount无限重渲染默认会把当前页面地址写入 navigation statenextPathname/nextSearch这样用户重新登录后会回到登出前的页面。getIdentity获取当前用户身份项目说明用途获取当前用户身份必选否使用场景总是请求格式-响应格式{ id: string \| number, fullName?: string, avatar?: string }错误格式ErrorAdmin 组件经常根据当前用户身份调整行为例如锁系统lock system可能只允许锁的拥有者编辑记录用户菜单AppBar.md 中的 UserMenu 说明需要展示当前用户的姓名和头像。react-admin 把已登录用户身份的存储委托给authProvider。只要它暴露了getIdentity()方法react-admin 就会调用它读取用户详情。getIdentity至少需要返回带id字段的对象也可以附带fullName、avatar或任何应用需要的自定义字段const authProvider { async getIdentity() { const authCredentials JSON.parse(localStorage.getItem(auth)); const { id, fullName, avatar } authCredentials; return { id, fullName, avatar }; }, // ... };fullName与avatar图片地址或>import { useGetIdentity, useGetOne } from react-admin; const PostDetail ({ id }) { const { data: post, isPending: postLoading } useGetOne(posts, { id }); const { identity, isPending: identityLoading } useGetIdentity(); if (postLoading || identityLoading) return Loading.../; if (!post.lockedBy || post.lockedBy identity.id) { // 文章未锁定或由我锁定可以编辑 return PostEdit post{post} / } else { // 文章被他人锁定只能查看 return PostShow post{post} / } }handleCallback处理第三方认证回调项目说明用途处理第三方认证服务Auth0、Cognito 等的回调必选否使用场景第三方认证流程成功时重定向到之前的页面或 admin 首页可自定义失败时渲染错误请求格式-响应格式void \| { redirectTo?: string \| boolean }错误格式Error当集成 Auth0 这类第三方认证服务时react-admin 提供了/auth-callback路由作为认证服务中的回调地址用户登录完成后会被重定向回该路由而它会在挂载时调用authProvider.handleCallback()。因此你可以在handleCallback中解析第三方服务通过 query 参数传回的信息例如取回 token。以下是一个使用 Auth0 的完整示例import { PreviousLocationStorageKey } from react-admin; import { Auth0Client } from ./Auth0Client; const authProvider { async login() { /* 这里无事可做该函数永远不会被调用 */ }, async checkAuth() { const isAuthenticated await client.isAuthenticated(); if (isAuthenticated) { return; } // 未认证保存用户试图访问的地址 localStorage.setItem(PreviousLocationStorageKey, window.location.href); // 然后重定向用户到 Auth0 服务 client.loginWithRedirect({ authorizationParams: { // 登录完成后Auth0 会把用户重定向回此页面 redirect_uri: ${window.location.origin}/auth-callback, }, }); }, // 用户在 Auth0 上登录成功后被重定向回应用的 /auth-callback 路由 async handleCallback() { const query window.location.search; if (!query.includes(code) !query.includes(state)) { throw new Error(Failed to handle login callback.); } // 如果收到了 Auth0 的参数就基于 query 参数获取 access token await Auth0Client.handleRedirectCallback(); }, ... }对应流程图见 Auth0 登录流程示意checkAuth在未认证时把当前地址写入localStorage再跳转到第三方服务用户完成登录后被带回/auth-callbackhandleCallback解析参数换取 token。handleCallbackresolve 后react-admin 会重定向到首页或重定向到localStorage.getItem(PreviousLocationStorageKey)记录的地址上面示例中正是checkAuth保存的用户原目标页。从 useHandleAuthCallback.ts 的源码可以确认重定向优先级为handleCallback返回的redirectTolocalStorage中的PreviousLocationStorageKey常量值为react-admin/nextPathname 默认首页若redirectTo false则不跳转。该钩子同样以 react-query 实现查询键为[auth, handleCallback]且retry: false。也可以通过返回值自定义跳转目标const authProvider { async handleCallback() { if (!query.includes(code) !query.includes(state)) { throw new Error(Failed to handle login callback.); } // 如果收到了 Auth0 的参数就基于 query 参数获取 access token await Auth0Client.handleRedirectCallback(); return { redirectTo: /posts }; }, // ... };提示如果完全依赖第三方认证流程authProvider.login()永远不会被调用此时提供一个总是 resolve 的空实现即可。canAccessAccess Control 式授权项目说明用途检查用户能否对某个资源执行某个操作必选否使用场景Access Control 式授权请求格式{ action: string, resource: string, record: object }响应格式boolean错误格式Errorreact-admin 内置了 Access Control 能力只需实现authProvider.canAccess()即可启用。它接收一个包含以下属性的权限对象action要对资源执行的操作如list、create、update、delete、showresource资源名称record可选执行操作所针对的记录canAccess()返回布尔值表示用户是否被允许对该资源执行该操作。如果canAccess抛错错误会被交给authProvider.checkError()处理。最简单的实现是对所有资源与操作放行const authProvider { async canAccess() { return true; }, // ... };更实际的场景是在登录时保存用户权限再据此比对请求的操作与资源const authProvider { async canAccess({ action, resource }) { // authorizedResources 形如 [posts, comments, users] const { authorizedResources } JSON.parse(localStorage.getItem(auth)); if (!authorizedResources.includes(resource)) { return false; } return true; }, // ... };从 useCanAccess.ts 的源码可以看到该能力的底层用法useCanAccess({ resource, action })钩子以[auth, canAccess, {...}]为查询键调用authProvider.canAccess()未实现canAccess时默认返回true并返回{ isPending, canAccess, error }供组件在渲染期间同步做访问控制。CanAccess、useCanAccessResources、useRequireAccess等组件与钩子见 ra-core/src/auth 目录都是建立在它之上的声明式/命令式封装。进阶方案RBAC 模块 基于canAccess提供了细粒度权限能力支持基于角色/权限的动态菜单、按钮级访问控制与自定义规则校验建议生产级应用直接复用。getPermissionsPermissions 式授权项目说明用途返回布尔值表示用户能否对资源执行提供的操作必选否使用场景Permissions 式授权请求格式传给usePermissions()的参数——react-admin 默认路由为空响应格式any错误格式Error作为canAccess()的替代方案getPermissions()允许你返回任意格式的权限对象React 组件可据此启用或禁用 UI 元素。权限可以是任何格式简单字符串如editor、字符串数组如[editor, admin]、或复杂对象如{ posts: editor, comments: moderator, users: admin }。const authProvider { async getPermissions({ action, resource }) { const { permissions } JSON.parse(localStorage.getItem(auth)); return permissions; }, // ... };react-admin 默认不主动使用权限但提供了 usePermissions 钩子 获取当前用户权限方便你在组件中实现自己的权限逻辑。选择建议canAccess与getPermissions如何取舍官方推荐 Access Control即canAccess因为它把授权逻辑放在authProvider中而非散落在 React 代码里更集中、更易测试与维护。Query Cancellation为 authProvider 启用请求取消react-admin 支持 Query Cancellation组件卸载时它发起的进行中查询会被取消从而避免过期的副作用与不必要的网络请求。要在 authProvider 中启用该能力需要为其设置supportAbortSignal属性为trueconst authProvider { /* ... */ }; authProvider.supportAbortSignal true;启用后authProvider 的每次调用都会收到额外的signal参数一个 AbortSignal 实例你必须把它透传给 fetch 调用const authProvider { async canAccess({ resource, action, record, signal }) { const url ${API_URL}/can_access?resource${resource}action${action}; const res await fetch(url, { signal }); if (!res.ok) { throw new HttpError(res.statusText); } return res.json(); }, }部分第三方 authProvider 已自带请求取消支持使用前请查阅其文档。另需注意开发环境下如果应用使用了React.StrictMode启用查询取消会令 API 查询加倍dev-only生产环境不会出现。从类型到实现的快速总览方法必选典型调用时机成功失败login✅提交登录表单重定向可自定义展示错误通知checkError✅每次 dataProvider 返回错误继续登出 重定向可自定义checkAuth✅每次导航到受保护路由继续登出 重定向可自定义logout✅点击登出按钮 / 会话失效重定向到登录页-getIdentity-需要展示用户身份时返回{ id, fullName?, avatar? }ErrorhandleCallback-第三方认证回调路由挂载时重定向渲染错误canAccess-Access Control 式授权返回boolean交给checkErrorgetPermissions-Permissions 式授权返回任意权限数据Error完整的类型定义可在 AuthProvider 类型 中查阅所有认证相关钩子的实现与测试用例集中在 ra-core/src/auth包括useLogin、useLogout、useCheckAuth、useGetIdentity、useCanAccess、useHandleAuthCallback、usePermissions等及其配套 spec 测试。更多相关主题可继续阅读 Authentication.md认证整体流程与外部认证提供方、Permissions.md两种授权模型对比、AuthRBAC.mdRBAC 细粒度授权与 useAuthProvider.md在组件中获取 authProvider 实例。【免费下载链接】react-adminA frontend Framework for single-page applications on top of REST/GraphQL APIs, using TypeScript, React and Material Design项目地址: https://gitcode.com/gh_mirrors/re/react-admin创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表