在 React 中,没有像 Vue 或 Angular 那样的内置路由守卫功能。但是,我们可以使用 React Router 或其他第三方库,结合一些自定义逻辑来实现类似的功能,接下来就让我们试试看吧。
JWT逻辑思维导图
结合导图,能更可观的了解整个流程,下面我们就直接上实操。
示例
以下是基于 React Router 实现路由守卫的基本示例:
假设:使用react-redux统一管理用户的状态
- 安装 React Router
csharp
npm install react-router-dom react-redux
# 或者
yarn add react-router-dom react-redux
- 定义一个外部处理高阶Layout:
创建一个自定义的 Layout
组件,该组件在渲染之前会检查某些条件(例如用户是否已登录)。如果条件不满足,则重定向到另一个页面(例如登录页)。
获取状态管理中的用户状态,判断是否存在Token
,存在则拉取用户信息,并更新用户信息状态
typescript
import { useSelector, useDispatch } from "react-redux";
export const Layout = (
props: any
) => {
const dispatch = useDispatch()
const user = useSelector<any, { userInfo: UserInfo, token: string }>((state) => state.UserReduce)
useEffect(() => {
if (user.token) {
getUserInfo<{ roles: string[], avatar: string, name: string }>()
.then(res => {
dispatch(setUserInfo(res.data))
})
}
}, [user.token])
}
路由守卫逻辑:
-
使用
useMemo
Hook 来记忆(或缓存)根据路由和 token 决定的组件渲染结果。 -
component
变量存储了最终要渲染的组件或 null(表示需要重定向或不做任何操作)。 -
路由守卫逻辑:
- 如果 token 存在且当前路由是登录页(
'/login'
),则使用history.go(-1)
返回上一页。 - 如果 token 不存在且当前路由不是登录页,则使用
window.location.replace
重定向到登录页,并带上当前路由作为查询参数。 - 如果以上两种情况都不满足,则渲染
props.children
(即传递给Layout
组件的子组件)。
- 如果 token 存在且当前路由是登录页(
typescript
export const Layout = (
props: any
) => {
const location = useLocation()
const dispatch = useDispatch()
const history = createBrowserHistory()
const user = useSelector<any, { userInfo: UserInfo, token: string }>((state) => state.UserReduce) // redux数据
useEffect(() => {
if (user.token) {
getUserInfo<{ roles: string[], avatar: string, name: string }>()
.then(res => {
dispatch(setUserInfo(res.data))
if (res.code === 401) { // 清除token,并返回登录页
}
})
}
}, [user.token])
const component = useMemo(() => { // 监听路由变化------------ 路由守卫
const { pathname, search } = location
const token = user.token
if(token && pathname === '/login') {
history.go(-1)
return null
}
if(!token && pathname !== '/login') {
window
.location
.replace(`/login?redirect=${pathname}${search ? search : ''}`)
return null
}
return props.children
}, [location.pathname])
return component
}
- 定义一个路由管理组件RouteLink:
定义 onEnter
函数
onEnter
是一个使用useCallback
Hook 定义的函数,它接收一个Component
作为参数。- 这个函数返回一个新的 JSX 元素,该元素是一个
Layout
组件,Layout
组件内部包裹了传入的Component
。 useCallback
的依赖项数组为空,因为onEnter不依赖于任何外部变量。
javascript
/ 假设 ASSETS_MENUS 在组件外部已经定义好,并且是一个数组,每个元素都有 path 和 element 属性
// const ASSETS_MENUS = [
// { path: '/', element: <HomePage /> },
// { path: '/about', element: <AboutPage /> },
// { path: '/login', element: <Login /> }
// // ... 其他路由配置
// ];
const RouterLink = () => {
const onEnter = React.useCallback((Component) => {
return <Layout><Component /></Layout>;
}, []); // 依赖项数组为空,因为onEnter不依赖于任何外部变量
return (
<div>
<BrowserRouter>
<Routes>
{ASSETS_MENUS.map((comp) => (
<Route
key={comp.path}
path={comp.path}
element={onEnter(comp.element)}
/>
))}
</Routes>
</BrowserRouter>
</div>
);
};
- APP入口中使用
直接使用就行了。
javascript
const App = () => {
return (
<div>
// 其他
<RouterLink />
</div>
);
};
路由守卫只是前端的安全措施之一。仍然需要在后端实施适当的安全措施,以确保即使前端逻辑被绕过,敏感数据也不会被未经授权的用户访问。