身份验证是 Web 应用程序中一项极其常见的需求。在本指南中,我们将介绍如何使用 TanStack Router 来构建受保护的路由,以及如何在用户尝试访问它们时将用户重定向到登录页面。
route.beforeLoad 选项允许您指定一个函数,该函数将在路由加载之前调用。它接收与 route.loader 函数相同的参数。这是一个检查用户是否已认证,并在未认证的情况下将他们重定向到登录页面的绝佳位置。
beforeLoad
函数的运行顺序与以下其他路由加载函数相对
需要注意的是,路由的 beforeLoad 函数会在其任何子路由的 beforeLoad 函数之前被调用。 它本质上是该路由及其所有子路由的中间件函数。
如果在 beforeLoad 中抛出错误,其任何子路由都不会尝试加载.
虽然不是必需的,但某些身份验证流程需要重定向到登录页面。要做到这一点,您可以从 beforeLoad 中 **抛出一个 redirect()**。
// src/routes/_authenticated.tsx
export const Route = createFileRoute('/_authenticated')({
beforeLoad: async ({ location }) => {
if (!isAuthenticated()) {
throw redirect({
to: '/login',
search: {
// Use the current location to power a redirect after login
// (Do not use `router.state.resolvedLocation` as it can
// potentially lag behind the actual current location)
redirect: location.href,
},
})
}
},
})
// src/routes/_authenticated.tsx
export const Route = createFileRoute('/_authenticated')({
beforeLoad: async ({ location }) => {
if (!isAuthenticated()) {
throw redirect({
to: '/login',
search: {
// Use the current location to power a redirect after login
// (Do not use `router.state.resolvedLocation` as it can
// potentially lag behind the actual current location)
redirect: location.href,
},
})
}
},
})
提示
redirect() 函数接受与 navigate 函数相同的选项,因此您可以传递 replace: true 等选项,如果您想替换当前历史条目而不是添加新条目。
一旦您认证了用户,将他们重定向回他们尝试访问的页面也是一种常见的做法。要做到这一点,您可以使用我们在原始重定向中添加的 redirect 查询参数。由于我们将用原始 URL 替换整个 URL,因此 router.history.push 比 router.navigate 更适合此操作。
router.history.push(search.redirect)
router.history.push(search.redirect)
一些应用程序选择不将用户重定向到登录页面,而是让用户停留在同一页面上,并显示一个登录表单,该表单会替换主内容或通过模态框隐藏它。通过简单地短路渲染通常会渲染子路由的 <Outlet />,也可以使用 TanStack Router 来实现这一点。
// src/routes/_authenticated.tsx
export const Route = createFileRoute('/_authenticated')({
component: () => {
if (!isAuthenticated()) {
return <Login />
}
return <Outlet />
},
})
// src/routes/_authenticated.tsx
export const Route = createFileRoute('/_authenticated')({
component: () => {
if (!isAuthenticated()) {
return <Login />
}
return <Outlet />
},
})
这可以让用户停留在同一页面上,同时仍然允许您渲染登录表单。一旦用户通过身份验证,您就可以简单地渲染 <Outlet />,子路由就会被渲染。
如果您的身份验证流程依赖于与 React context 和/或 hooks 的交互,您需要使用 router.context 选项将您的身份验证状态传递给 TanStack Router。
重要
React hooks 不应该在 React 组件之外使用。如果您需要在 React 组件之外使用 hook,您需要在一个包装了 <RouterProvider /> 的组件中提取 hook 返回的状态,然后将返回的值传递给 TanStack Router。
我们将在 Router Context 部分详细介绍 router.context 选项。
以下是一个使用 React context 和 hooks 来保护 TanStack Router 中已认证路由的示例。请在 Authenticated Routes 示例 中查看完整的可运行设置。
import { createRootRouteWithContext } from '@tanstack/solid-router'
interface MyRouterContext {
// The ReturnType of your useAuth hook or the value of your AuthContext
auth: AuthState
}
export const Route = createRootRouteWithContext<MyRouterContext>()({
component: () => <Outlet />,
})
import { createRootRouteWithContext } from '@tanstack/solid-router'
interface MyRouterContext {
// The ReturnType of your useAuth hook or the value of your AuthContext
auth: AuthState
}
export const Route = createRootRouteWithContext<MyRouterContext>()({
component: () => <Outlet />,
})
import { createRouter } from '@tanstack/solid-router'
import { routeTree } from './routeTree.gen'
export const router = createRouter({
routeTree,
context: {
// auth will initially be undefined
// We'll be passing down the auth state from within a React component
auth: undefined!,
},
})
import { createRouter } from '@tanstack/solid-router'
import { routeTree } from './routeTree.gen'
export const router = createRouter({
routeTree,
context: {
// auth will initially be undefined
// We'll be passing down the auth state from within a React component
auth: undefined!,
},
})
import { RouterProvider } from '@tanstack/solid-router'
import { AuthProvider, useAuth } from './auth'
import { router } from './router'
function InnerApp() {
const auth = useAuth()
return <RouterProvider router={router} context={{ auth }} />
}
function App() {
return (
<AuthProvider>
<InnerApp />
</AuthProvider>
)
}
import { RouterProvider } from '@tanstack/solid-router'
import { AuthProvider, useAuth } from './auth'
import { router } from './router'
function InnerApp() {
const auth = useAuth()
return <RouterProvider router={router} context={{ auth }} />
}
function App() {
return (
<AuthProvider>
<InnerApp />
</AuthProvider>
)
}
然后在已认证路由中,您可以使用 beforeLoad 函数检查身份验证状态,如果用户未登录,则 **抛出一个 redirect()** 到您的 **Login 路由**。
import { createFileRoute, redirect } from '@tanstack/solid-router'
export const Route = createFileRoute('/dashboard')({
beforeLoad: ({ context, location }) => {
if (!context.auth.isAuthenticated) {
throw redirect({
to: '/login',
search: {
redirect: location.href,
},
})
}
},
})
import { createFileRoute, redirect } from '@tanstack/solid-router'
export const Route = createFileRoute('/dashboard')({
beforeLoad: ({ context, location }) => {
if (!context.auth.isAuthenticated) {
throw redirect({
to: '/login',
search: {
redirect: location.href,
},
})
}
},
})
您还可以 **选择** 使用 非重定向认证 方法来显示登录表单,而不是调用 **redirect**。
此方法还可以与 Pathless 或 Layout Route 结合使用,以保护其父路由下的所有路由。
有关详细的分步实现指南,请参阅
存储库中提供了可用的身份验证示例
您的每周 JavaScript 资讯。每周一免费发送给超过 10 万开发者。