如何在SolidStart中实现客户端导航时的强制认证与重定向?
在SolidStart 1.1.0中实现可靠的路由认证重定向
问题背景
我用SolidStart 1.1.0构建项目,采用基于Cookie的会话认证方式。为校验用户登录状态,写了一个服务端查询函数getUser(),未认证时会抛出重定向到/Login的异常:
// server/auth.ts export const getUser = query(async () => { "use server" const auth = await getAuthSession() if (!auth) throw redirect("/Login") return auth }, "user")
在受保护路由(比如/dashboard)里,我用createAsync获取用户信息:
// routes/dashboard.tsx import { getUser } from "~/server/auth" export default function Dashboard() { const user = createAsync(() => getUser()) return ( <main> <h1>Welcome, {user()?.name}</h1> </main> ) }
遇到的问题:
- 硬刷新或直接输入URL时正常,未认证会自动跳转到登录页
- 客户端路由导航(比如点击页面内链接)时,SolidStart会复用
getUser的缓存结果:- 不会重新执行
query()函数 - 不会触发
throw redirect("/Login") - 页面加载后
user()为null,但不会跳转
- 不会重新执行
试过用createEffect或onMount手动检查并重定向,但有两个问题:
- 没法区分
user()为null是加载中还是未认证状态 - 有时时序不对,已登录用户也会被误重定向
解决方案
方案1:禁用查询缓存,强制每次验证
修改getUser的查询配置,设置revalidate: 0或者cache: "no-store",让每次客户端导航都重新执行认证检查:
// server/auth.ts export const getUser = query(async () => { "use server" const auth = await getAuthSession() if (!auth) throw redirect("/Login") return auth }, { key: "user", revalidate: 0, // 也可以用 cache: "no-store" })
方案2:用路由加载器处理认证(推荐)
SolidStart的路由加载器(routeData)会在服务端渲染和客户端导航时都执行,是更可靠的方式:
// routes/dashboard.tsx import { getUser } from "~/server/auth" import { createRouteData } from "solid-start" // 路由加载器,组件渲染前先执行认证检查 export const routeData = () => { return createRouteData(async () => { return await getUser() }) } export default function Dashboard() { const user = routeData() return ( <main> <h1>Welcome, {user()?.name}</h1> </main> ) }
只要getUser抛出重定向,SolidStart会自动处理跳转,不会出现缓存导致的失效问题。
方案3:手动管理缓存与状态区分
如果需要保留缓存,可以在客户端导航时手动失效缓存,同时区分加载状态:
// routes/dashboard.tsx import { getUser } from "~/server/auth" import { createAsync, useNavigate, useLocation } from "solid-start" import { invalidateQuery } from "@solidjs/router" export default function Dashboard() { const navigate = useNavigate() const location = useLocation() const user = createAsync(() => getUser()) // 客户端路由变化时,失效用户查询缓存 createEffect(() => { location.pathname invalidateQuery("user") }) // 仅在加载完成且用户为null时重定向 createEffect(() => { const currentUser = user() if (!user.loading && currentUser === null) { navigate("/Login") } }) // 加载状态提示 if (user.loading) { return <div>Loading...</div> } return ( <main> <h1>Welcome, {user()?.name}</h1> </main> ) }
内容的提问来源于stack exchange,提问作者thePooyan
相关产品推荐
相关产品推荐

