Ver Fonte

feat: 重定向页面

BaiLuoYan há 3 meses atrás
pai
commit
92886cb1d8

+ 3 - 3
src/config/request/authHeaderInterceptor.ts

@@ -58,11 +58,11 @@ export const authHeaderInterceptor: IRequestInterceptorAxios = (config: RequestC
     if (config.requireToken !== true) return config;
 
     const data = getToken();
-    const expires = (data.expires ?? 0) - currentUnixTimestamp() > 0;
-    if (data.accessToken && expires) {
+    const expires = (data?.expires ?? 0) - currentUnixTimestamp() > 0;
+    if (data?.accessToken && expires) {
         config.headers['Authorization'] = formatToken(data.accessToken);
         return config;
     } else {
-        return TokenRefresh.beforeRequestRefreshTokenFirst(config, data.refreshToken!);
+        return TokenRefresh.beforeRequestRefreshTokenFirst(config, data?.refreshToken ?? 'none'); // 如果 refreshToken 为空,接口会返回 401 错误,跳转到登录页
     }
 };

+ 68 - 0
src/pages/redirect/index.tsx

@@ -0,0 +1,68 @@
+import React, { useEffect } from 'react';
+
+import { useNavigate, useSearchParams } from 'react-router-dom';
+
+import { setToken } from '@/utils/authUtils';
+
+/**
+ * 解密重定向参数
+ * @param _encryptedData 加密的重定向参数
+ * @returns 解密后的数据,包含 UserInfo 和路由信息
+ */
+const decryptRedirectParams = async (
+    _encryptedData: string
+): Promise<{ userInfo: API.UserInfo; redirectPath: string } | null> => {
+    // TODO: 实现解密逻辑
+    // 这里先返回 null,等待后续实现
+    return null;
+};
+
+const Redirect: React.FC = () => {
+    const navigate = useNavigate();
+    const [searchParams] = useSearchParams();
+
+    useEffect(() => {
+        const redirectParam = searchParams.get('d');
+
+        // 如果没有重定向参数,默认跳转到 home 页
+        if (!redirectParam) {
+            navigate('/home', { replace: true });
+            return;
+        }
+
+        // 如果有重定向参数,解密并处理
+        const handleRedirect = async () => {
+            try {
+                // 解密重定向参数
+                const decryptedData = await decryptRedirectParams(redirectParam);
+
+                if (!decryptedData) {
+                    // 解密失败,跳转到 home 页
+                    navigate('/home', { replace: true });
+                    return;
+                }
+
+                const { userInfo, redirectPath } = decryptedData;
+
+                // 保存用户信息到 localStorage
+                if (userInfo) {
+                    setToken(userInfo);
+                }
+
+                // 跳转到指定路由
+                navigate(redirectPath || '/home', { replace: true });
+            } catch (error) {
+                console.error('处理重定向参数失败:', error);
+                // 出错时跳转到 home 页
+                navigate('/home', { replace: true });
+            }
+        };
+
+        handleRedirect();
+    }, [navigate, searchParams]);
+
+    // 空白页面,不显示任何内容
+    return null;
+};
+
+export default Redirect;

+ 7 - 0
src/router/routes.tsx

@@ -4,6 +4,7 @@ import Layout from '@/layouts/BasicLayout';
 import Forbidden from '@/pages/error/403';
 import NotFound from '@/pages/error/404';
 import ServerError from '@/pages/error/500';
+import Redirect from '@/pages/redirect';
 import Home from '@/pages/home';
 import RouteDemo from '@/pages/routeDemo';
 
@@ -46,6 +47,12 @@ const routes: AppRouteObject[] = [
             },
         ],
     },
+    {
+        name: 'to',
+        path: '/to',
+        inMenu: false,
+        element: <Redirect />,
+    },
     {
         name: '403',
         path: '/403',

+ 10 - 4
src/services/login/typings.d.ts

@@ -6,16 +6,22 @@ declare namespace API {
         refreshToken?: string;
         /** `accessToken`的过期时间(unix时间戳) */
         expires?: number;
+        /** 用户ID */
+        userId?: string;
+        /** 设备ID */
+        deviceId?: string;
         /** 用户名 */
         username?: string;
         /** 昵称 */
         nickname?: string;
         /** 头像 */
         avatar?: string;
-        /** 当前登录用户的角色 */
-        roles?: Array<string>;
-        /** 当前登录用户的按钮级别权限 */
-        permissions?: Array<string>;
+        /** 邮箱 */
+        email?: string;
+        /** 手机号 */
+        phone?: string;
+        /** 推荐码 */
+        refer?: string;
     };
 
     type LoginParams = {

+ 46 - 24
src/utils/authUtils.ts

@@ -10,15 +10,17 @@ export const userKey = 'user-info';
 export const tokenKey = 'authorized-token';
 
 /** 获取`token` */
-export function getToken(): API.UserInfo | null {
-    return Cookies.get(tokenKey) ? JSON.parse(Cookies.get(tokenKey)!) : ls.getLocal(userKey);
+export function getToken(): API.UserInfo {
+    return Cookies.get(tokenKey)
+        ? JSON.parse(Cookies.get(tokenKey)!)
+        : (ls.getLocal(userKey) as API.UserInfo);
 }
 
 /**
- * @description 设置`token`以及一些必要信息并采用无感刷新`token`方案
- * 无感刷新:后端返回`accessToken`(访问接口使用的`token`)、`refreshToken`(用于调用刷新`accessToken`的接口时所需的`token`,`refreshToken`的过期时间(比如30天)应大于`accessToken`的过期时间(比如2小时))、`expires`(`accessToken`的过期时间)
- * 将`accessToken`、`expires`、`refreshToken`这三条信息放在key值为authorized-token的cookie里(过期自动销毁)
- * 将`username`、`nickname`、`avatar`、`roles`、`permissions`、`refreshToken`、`expires`这七条信息放在key值为`user-info`的localStorage
+ * @description 设置 token 以及一些必要信息并采用无感刷新 token 方案
+ * 无感刷新:后端返回 accessToken(访问接口使用的 token)、refreshToken(用于调用刷新 accessToken 的接口时所需的 token,refreshToken 的过期时间(比如30天)应大于 accessToken 的过期时间(比如2小时))、expires(accessToken 的过期时间)
+ * 将 accessToken、expires、refreshToken 这三条信息放在 key 值为 authorized-token 的 cookie 里(过期自动销毁)
+ * 将除了 accessToken 之外的其他信息放在 key 值为 user-info 的 localStorage 
  */
 export function setToken(data: API.UserInfo) {
     const { accessToken = '', expires = 0, refreshToken = '' } = data;
@@ -32,35 +34,55 @@ export function setToken(data: API.UserInfo) {
         Cookies.set(tokenKey, cookieString);
     }
 
-    function setUserKey({ avatar, username, nickname, roles, permissions }: API.UserInfo) {
+    function setUserKey({
+        userId,
+        deviceId,
+        username,
+        nickname,
+        avatar,
+        email,
+        phone,
+        refer,
+    }: API.UserInfo) {
         ls.setLocal(userKey, {
+            refreshToken,
+            expires,
+            userId,
+            deviceId,
             username,
             nickname,
             avatar,
-            roles,
-            permissions,
-            refreshToken,
-            expires,
+            email,
+            phone,
+            refer,
         });
     }
 
-    if (data.username && data.roles) {
-        const { username, roles } = data;
+    if (data.userId || data.deviceId) {
+        // 登录时,后端接口会返回用户信息,直接设置到 localStorage 中
         setUserKey({
-            username,
-            nickname: data?.nickname ?? '',
-            avatar: data?.avatar ?? '',
-            roles,
-            permissions: data?.permissions ?? [],
+            userId: data.userId || '',
+            deviceId: data.deviceId || '',
+            username: data.username || '',
+            nickname: data.nickname || '',
+            avatar: data.avatar || '',
+            email: data.email || '',
+            phone: data.phone || '',
+            refer: data.refer || '',
         });
     } else {
+        // 刷新 token 时,后端接口不会返回用户信息,需要从 localStorage 中获取用户信息
         const d = ls.getLocal<API.UserInfo>(userKey);
-        const username = d?.username ?? '';
-        const nickname = d?.nickname ?? '';
-        const avatar = d?.avatar ?? '';
-        const roles = d?.roles ?? [];
-        const permissions = d?.permissions ?? [];
-        setUserKey({ username, nickname, avatar, roles, permissions });
+        setUserKey({
+            userId: d?.userId || '',
+            deviceId: d?.deviceId || '',
+            username: d?.username || '',
+            nickname: d?.nickname || '',
+            avatar: d?.avatar || '',
+            email: d?.email || '',
+            phone: d?.phone || '',
+            refer: d?.refer || '',
+        });
     }
 }