Skip to content

Commit 9c4725e

Browse files
committed
feat: 添加权限校验
1 parent d56dad8 commit 9c4725e

File tree

1 file changed

+146
-6
lines changed

1 file changed

+146
-6
lines changed

src/pages/layout.tsx

Lines changed: 146 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,168 @@
1-
import { Outlet } from 'react-router-dom';
2-
import type { LoaderFunctionArgs } from 'react-router-dom';
1+
import type { RoutePath } from '@soybean-react/vite-plugin-react-router';
2+
import { Outlet, matchRoutes } from 'react-router-dom';
33

4+
import { isStaticSuper, selectUserInfo } from '@/features/auth/authStore';
45
import { useRoute } from '@/features/router';
6+
import { routeMap } from '@/router/elegant/routeMap';
7+
import { localStg } from '@/utils/storage';
8+
9+
const allRoutes = Object.values(routeMap)
10+
.slice(1)
11+
.map(route => ({
12+
path: route
13+
}));
14+
15+
/**
16+
* initialize route
17+
*
18+
* @param to to route
19+
*/
20+
function initRoute(to: Router.Route) {
21+
const notFoundRoute = 'notFound';
22+
23+
const isNotFoundRoute = to.id === notFoundRoute;
24+
25+
const isLogin = Boolean(localStg.get('token'));
26+
27+
if (!isLogin) {
28+
// if the user is not logged in and the route is a constant route but not the "not-found" route, then it is allowed to access.
29+
if (to.handle.constant && !isNotFoundRoute) {
30+
return null;
31+
}
32+
33+
// if the user is not logged in, then switch to the login page
34+
const loginRoute: RoutePath = '/login';
35+
const query = to.fullPath;
36+
37+
const location = `${loginRoute}?redirect=${query}`;
38+
39+
return location;
40+
}
41+
42+
// the auth route is initialized
43+
// it is not the "not-found" route, then it is allowed to access
44+
if (!isNotFoundRoute) {
45+
return null;
46+
}
47+
48+
// it is captured by the "not-found" route, then check whether the route exists
49+
const exist = matchRoutes(allRoutes, to.pathname);
50+
51+
const noPermissionRoute = '/403';
52+
53+
if (exist && exist.length > 0) {
54+
const fullPath = noPermissionRoute;
55+
56+
return fullPath;
57+
}
58+
59+
return null;
60+
}
61+
62+
function handleRouteSwitch(to: Router.Route, from: Router.Route | null) {
63+
// route with href
64+
if (to.handle.href) {
65+
window.open(to.handle.href, '_blank');
66+
67+
return { path: from?.fullPath, replace: true };
68+
}
69+
70+
return null;
71+
}
72+
73+
function createRouteGuard(to: Router.Route, roles: string[], isSuper: boolean) {
74+
const rootRoute: RoutePath = '/';
75+
const loginRoute: RoutePath = '/login';
76+
const noAuthorizationRoute: RoutePath = '/403';
77+
78+
const isLogin = Boolean(localStg.get('token'));
79+
const needLogin = !to.handle.constant;
80+
const routeRoles = to.handle.roles || [];
81+
82+
const hasRole = roles.some(role => routeRoles.includes(role));
83+
84+
const hasAuth = isSuper || !routeRoles.length || hasRole;
85+
86+
// if it is login route when logged in, then switch to the root page
87+
if (to.fullPath.includes('login') && isLogin) {
88+
return rootRoute;
89+
}
90+
91+
if (to.id === 'notFound') {
92+
const exist = matchRoutes(allRoutes, to.pathname);
93+
94+
if (exist && exist.length > 0) {
95+
return noAuthorizationRoute;
96+
}
97+
98+
return null;
99+
}
100+
101+
if (!needLogin) return handleRouteSwitch(to, to.redirect);
102+
103+
// the route need login but the user is not logged in, then switch to the login page
104+
if (!isLogin) return `${loginRoute}?redirect=${to.fullPath}`;
105+
106+
// if the user is logged in but does not have authorization, then switch to the 403 page
107+
if (!hasAuth) return noAuthorizationRoute;
108+
109+
return handleRouteSwitch(to, to.redirect);
110+
}
5111

6112
const RootLayout = () => {
7-
const { fullPath, handle } = useRoute();
113+
const route = useRoute();
8114

9-
const { t } = useTranslation();
115+
const inInit = useRef(false);
116+
117+
const { fullPath, handle } = route;
10118

11-
useLayoutEffect(() => {}, []);
119+
const { roles } = useAppSelector(selectUserInfo);
120+
121+
const isSuper = useAppSelector(isStaticSuper);
122+
123+
const { t } = useTranslation();
12124

13125
useEffect(() => {
14126
const { i18nKey, title } = handle;
15127

16128
document.title = i18nKey ? t(i18nKey) : title;
17129

18130
window.NProgress?.done?.();
131+
19132
// eslint-disable-next-line react-hooks/exhaustive-deps
20133
}, [fullPath]);
21134

135+
if (!inInit.current) {
136+
inInit.current = true;
137+
138+
const location = initRoute(route);
139+
140+
if (location) {
141+
return <Navigate to={location} />;
142+
}
143+
}
144+
145+
const location = createRouteGuard(route, roles, isSuper);
146+
147+
if (location && inInit.current) {
148+
if (typeof location === 'string') {
149+
return <Navigate to={location} />;
150+
}
151+
152+
if (location.path) {
153+
return (
154+
<Navigate
155+
replace={location.replace}
156+
to={location.path}
157+
/>
158+
);
159+
}
160+
}
161+
22162
return <Outlet />;
23163
};
24164

25-
export const loader = async ({ params, request }: LoaderFunctionArgs) => {
165+
export const loader = () => {
26166
window.NProgress?.start?.();
27167

28168
return null;

0 commit comments

Comments
 (0)