一种可能的解决方案的简短摘要:
- 将所有需要身份验证的路由包装在将未经身份
<ProtectedRoute>
验证的用户重定向到登录页面的组件内。ProtectedRoute-component 只检查用户是否有有效的令牌,如果没有则重定向用户。
- 内部错误链接首先删除令牌,或者以某种方式取消验证它,然后调用
location.reload()
下面详细实现。
我找不到任何直接的解决方案。在正常情况下,为了重定向用户,我使用 react-router navigate() 钩子。在错误链接中,我发现无法使用 react-hooks。
但是,我设法解决了实际问题。我实现了 ProtectedRoute 组件,它包装了需要身份验证的应用程序的所有部分:
type ProtectedRouteProps = {
path: string;
toRedirect: string;
};
export const ProtectedRoute: FunctionComponent<ProtectedRouteProps> = ({
path,
toRedirect,
children,
}) => {
return isAuthenticated() ? (
<Route path={path}>
{children}
</Route>
) : (
<Navigate to={{ pathname: toRedirect }} />
);
};
type ValidToken = string;
type ExpiredToken = 'Expired token'
type NullToken = '' | null
export type JwtTokenType = (ValidToken | ExpiredToken | NullToken )
export const isNullToken = (token: JwtTokenType) : boolean => {
return (token === '' || token === null)
}
export const isExpiredToken = (token: JwtTokenType) : boolean => {
return token === "Expired token"
}
export const isAuthenticated = () : boolean => {
let token = getTokenFromCookies();
return !(isExpiredToken(token) || isNullToken(token));
}
我使用它如下:
<Routes>
<Route path="login" element={<LoginPage />} />
<ProtectedRoute path="/*" toRedirect="login">
// protected routes here
</ProtectedRoute>
</Routes>
为了处理未经身份验证的用户的注销和重定向,我实现了两个功能:
// Use this in normal cases
export function useHandleLogout(): () => void {
const navigate = useNavigate();
// maybe call other hooks
});
function handleLogout() {
navigate("/login");
removeToken();
// do other stuff you want
}
return handleLogout;
}
// Use this inside error-link
export const handleLogoutWithoutHook = () => {
// Logout without hook
removeToken();
// do other stuff required when logout
// eslint-disable-next-line no-restricted-globals
location.reload();
// location.reload() after token removed affects user redirect
// when component is wrapped inside <ProtectedRoute> component
};
export const removeToken = () => {
Cookies.remove("jwt-token")
}
最后在错误链接中:
export const errorLink = onError(
({ graphQLErrors, networkError, operation, forward }) => {
if (graphQLErrors) {
for (let err of graphQLErrors) {
if (err.message.includes('AnonymousUser')) {
handleLogoutWithoutHook()
return
}
if (err.message.includes('Signature has expired')) {
handleLogoutWithoutHook()
}
console.log(err.message)
}
}
return forward(operation)
}
);