feat: set jwt expired time to 30d
This commit is contained in:
1 parent
726c6c5e97
commit
4803321951
8 files changed
+223
-66
No files matched your search
@@ -0,0 +1,49 @@
|
||||
/**
|
||||
* 应用配置常量
|
||||
*/
|
||||
|
||||
/**
|
||||
* JWT 配置
|
||||
*/
|
||||
export const JWT_CONFIG = {
|
||||
/**
|
||||
* Access Token 有效期:30天
|
||||
* 用户登录后 30 天内无需重新登录
|
||||
*/
|
||||
ACCESS_TOKEN_EXPIRES_IN: "30d",
|
||||
|
||||
/**
|
||||
* 默认 JWT 密钥(生产环境应使用 CloudFlare 版本 ID)
|
||||
*/
|
||||
DEFAULT_SECRET: "your-secret-key-change-in-production",
|
||||
};
|
||||
|
||||
/**
|
||||
* 认证相关配置
|
||||
*/
|
||||
export const AUTH_CONFIG = {
|
||||
/**
|
||||
* Bcrypt 加密盐的轮数
|
||||
*/
|
||||
BCRYPT_SALT_ROUNDS: 10,
|
||||
|
||||
/**
|
||||
* 密码最小长度
|
||||
*/
|
||||
MIN_PASSWORD_LENGTH: 6,
|
||||
};
|
||||
|
||||
/**
|
||||
* 监控配置
|
||||
*/
|
||||
export const MONITOR_CONFIG = {
|
||||
/**
|
||||
* 监控检查间隔(分钟)
|
||||
*/
|
||||
CHECK_INTERVAL_MINUTES: 5,
|
||||
|
||||
/**
|
||||
* 请求超时时间(毫秒)
|
||||
*/
|
||||
REQUEST_TIMEOUT_MS: 30000,
|
||||
};
|
||||
@@ -1 +1,2 @@
|
||||
export * from "./db";
|
||||
export * from "./db";
|
||||
export * from "./constants";
|
||||
@@ -10,6 +10,7 @@ import { getJwtSecret } from "../utils/jwt";
|
||||
import { Bindings } from "../models/db";
|
||||
import * as SettingsService from "./SettingsService";
|
||||
import * as NotificationService from "./NotificationService";
|
||||
import { JWT_CONFIG, AUTH_CONFIG } from "../config";
|
||||
|
||||
/**
|
||||
* 用户登录
|
||||
@@ -78,7 +79,10 @@ export async function loginUser(
|
||||
const secret = getJwtSecret(env);
|
||||
console.log("获取到的 JWT secret:", secret);
|
||||
|
||||
const token = jsonwebtoken.sign(payload, secret, { expiresIn: "24h" });
|
||||
// 使用配置的 token 有效期(30天)
|
||||
const token = jsonwebtoken.sign(payload, secret, {
|
||||
expiresIn: JWT_CONFIG.ACCESS_TOKEN_EXPIRES_IN,
|
||||
} as jsonwebtoken.SignOptions) as string;
|
||||
console.log("成功生成 JWT token, 长度:", token.length);
|
||||
|
||||
return {
|
||||
@@ -121,7 +125,8 @@ export async function registerUser(
|
||||
): Promise<{ success: boolean; message: string; user?: any }> {
|
||||
try {
|
||||
// 新增:检查是否允许新用户注册
|
||||
const isRegistrationAllowed = await SettingsService.getAllowNewUserRegistration();
|
||||
const isRegistrationAllowed =
|
||||
await SettingsService.getAllowNewUserRegistration();
|
||||
if (!isRegistrationAllowed) {
|
||||
return { success: false, message: "新用户注册功能已关闭" };
|
||||
}
|
||||
@@ -132,8 +137,8 @@ export async function registerUser(
|
||||
return { success: false, message: "用户名已存在" };
|
||||
}
|
||||
|
||||
// 密码加密
|
||||
const salt = await bcrypt.genSalt(10);
|
||||
// 密码加密(使用配置的盐轮数)
|
||||
const salt = await bcrypt.genSalt(AUTH_CONFIG.BCRYPT_SALT_ROUNDS);
|
||||
const hashedPassword = await bcrypt.hash(password, salt);
|
||||
|
||||
// 创建用户, 默认角色为 'user'
|
||||
@@ -146,7 +151,9 @@ export async function registerUser(
|
||||
|
||||
// 为新用户创建默认的通知设置
|
||||
if (newUser && newUser.id) {
|
||||
await NotificationService.createDefaultNotificationSettingsForUser(newUser.id);
|
||||
await NotificationService.createDefaultNotificationSettingsForUser(
|
||||
newUser.id
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
* JWT工具类,提供JWT相关的通用功能
|
||||
*/
|
||||
|
||||
import { JWT_CONFIG } from "../config";
|
||||
|
||||
/**
|
||||
* 获取JWT密钥
|
||||
* 优先从环境变量中获取JWT_SECRET,如果不存在则使用默认值
|
||||
@@ -13,7 +15,7 @@ export const getJwtSecret = (c: any): string => {
|
||||
// 检查是否直接包含 CF_VERSION_METADATA
|
||||
if (c.CF_VERSION_METADATA) {
|
||||
const { id: versionId } = c.CF_VERSION_METADATA;
|
||||
return versionId || "your-secret-key-change-in-production";
|
||||
return versionId || JWT_CONFIG.DEFAULT_SECRET;
|
||||
}
|
||||
|
||||
// 检查是否在 env 属性下包含 CF_VERSION_METADATA
|
||||
@@ -21,11 +23,11 @@ export const getJwtSecret = (c: any): string => {
|
||||
console.log("context.env 包含 CF_VERSION_METADATA");
|
||||
const { id: versionId } = c.env.CF_VERSION_METADATA;
|
||||
console.log("解析出的 versionId:", versionId);
|
||||
return versionId || "your-secret-key-change-in-production";
|
||||
return versionId || JWT_CONFIG.DEFAULT_SECRET;
|
||||
}
|
||||
|
||||
console.error("错误: 未找到 CF_VERSION_METADATA");
|
||||
return "your-secret-key-change-in-production";
|
||||
return JWT_CONFIG.DEFAULT_SECRET;
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
import { Navigate, useLocation } from "react-router-dom";
|
||||
import { useAuth } from "../providers/AuthProvider";
|
||||
|
||||
interface ProtectedRouteProps {
|
||||
children: React.ReactNode;
|
||||
}
|
||||
|
||||
/**
|
||||
* 路由守卫组件
|
||||
* 用于保护需要登录才能访问的路由
|
||||
*/
|
||||
export default function ProtectedRoute({ children }: ProtectedRouteProps) {
|
||||
const { isAuthenticated, isLoading } = useAuth();
|
||||
const location = useLocation();
|
||||
|
||||
// 如果正在加载,显示加载状态
|
||||
if (isLoading) {
|
||||
return (
|
||||
<div className="flex items-center justify-center min-h-screen">
|
||||
<div className="text-center">
|
||||
<div className="animate-spin rounded-full h-12 w-12 border-b-2 border-gray-900 mx-auto"></div>
|
||||
<p className="mt-4 text-gray-600">加载中...</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// 如果未登录,重定向到登录页面,并保存当前路径
|
||||
if (!isAuthenticated) {
|
||||
return <Navigate to="/login" state={{ from: location }} replace />;
|
||||
}
|
||||
|
||||
// 已登录,渲染子组件
|
||||
return <>{children}</>;
|
||||
}
|
||||
@@ -14,11 +14,19 @@ const Login = () => {
|
||||
const [message, setMessage] = useState("");
|
||||
const [showRegister, setShowRegister] = useState(false); // 新增状态
|
||||
|
||||
const { login } = useAuth();
|
||||
const { login, isAuthenticated } = useAuth();
|
||||
const navigate = useNavigate();
|
||||
const location = useLocation();
|
||||
const { t } = useTranslation();
|
||||
|
||||
// 如果已登录,重定向到 dashboard 或原来要访问的页面
|
||||
useEffect(() => {
|
||||
if (isAuthenticated) {
|
||||
const from = (location.state as any)?.from?.pathname || "/dashboard";
|
||||
navigate(from, { replace: true });
|
||||
}
|
||||
}, [isAuthenticated, navigate, location]);
|
||||
|
||||
// 检查是否有来自注册页面的消息
|
||||
useEffect(() => {
|
||||
// 检查是否允许注册
|
||||
@@ -47,7 +55,9 @@ const Login = () => {
|
||||
try {
|
||||
const result = await login({ username, password });
|
||||
if (result.success) {
|
||||
navigate("/dashboard");
|
||||
// 登录成功后,重定向到用户原来要访问的页面,或默认到 dashboard
|
||||
const from = (location.state as any)?.from?.pathname || "/dashboard";
|
||||
navigate(from, { replace: true });
|
||||
} else {
|
||||
setError(result.message);
|
||||
}
|
||||
|
||||
@@ -11,12 +11,7 @@ import {
|
||||
getCurrentUser,
|
||||
} from "../api/auth";
|
||||
import { getAllowNewUserRegistration } from "../api/settings"; // 新增
|
||||
import {
|
||||
User,
|
||||
LoginRequest,
|
||||
RegisterRequest,
|
||||
AuthContextType,
|
||||
} from "../types";
|
||||
import { User, LoginRequest, RegisterRequest, AuthContextType } from "../types";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
const AuthContext = createContext<AuthContextType | undefined>(undefined);
|
||||
@@ -37,12 +32,39 @@ export const AuthProvider: React.FC<{ children: ReactNode }> = ({
|
||||
if (storedToken && storedUser) {
|
||||
setToken(storedToken);
|
||||
setUser(JSON.parse(storedUser));
|
||||
setIsLoading(false);
|
||||
// 验证 token 是否有效
|
||||
verifyToken();
|
||||
} else {
|
||||
setIsLoading(false);
|
||||
}
|
||||
}, []);
|
||||
|
||||
// 验证 token 是否有效
|
||||
const verifyToken = async () => {
|
||||
try {
|
||||
const response = await getCurrentUser();
|
||||
if (response.success && response.user) {
|
||||
setUser(response.user);
|
||||
localStorage.setItem("user", JSON.stringify(response.user));
|
||||
} else {
|
||||
// Token 无效,清除登录状态
|
||||
localStorage.removeItem("token");
|
||||
localStorage.removeItem("user");
|
||||
setToken(null);
|
||||
setUser(null);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(t("auth.error.fetchUser"), error);
|
||||
// Token 验证失败,清除登录状态
|
||||
localStorage.removeItem("token");
|
||||
localStorage.removeItem("user");
|
||||
setToken(null);
|
||||
setUser(null);
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
// 如果有 token,但没有 user,则获取用户信息
|
||||
if (token && !user) {
|
||||
@@ -93,7 +115,7 @@ export const AuthProvider: React.FC<{ children: ReactNode }> = ({
|
||||
// 客户端再次检查是否允许注册
|
||||
const allowResponse = await getAllowNewUserRegistration();
|
||||
if (!allowResponse.success || !allowResponse.allow) {
|
||||
return { success: false, message: t('register.disabled') };
|
||||
return { success: false, message: t("register.disabled") };
|
||||
}
|
||||
const response = await apiRegister(data);
|
||||
return { success: response.success, message: response.message };
|
||||
|
||||
@@ -5,6 +5,7 @@ import { lazy, Suspense, ReactNode } from "react";
|
||||
import Layout from "../components/Layout";
|
||||
import UsersList from "../pages/users/UsersList";
|
||||
import UserProfile from "../pages/users/UserProfile";
|
||||
import ProtectedRoute from "../components/ProtectedRoute";
|
||||
|
||||
// 懒加载页面组件
|
||||
const Dashboard = lazy(() => import("../pages/Dashboard"));
|
||||
@@ -55,9 +56,11 @@ const protectedRoutes: RouteObject[] = [
|
||||
{
|
||||
path: "/dashboard",
|
||||
element: (
|
||||
<Suspense fallback={<div>加载中...</div>}>
|
||||
<Dashboard />
|
||||
</Suspense>
|
||||
<ProtectedRoute>
|
||||
<Suspense fallback={<div>加载中...</div>}>
|
||||
<Dashboard />
|
||||
</Suspense>
|
||||
</ProtectedRoute>
|
||||
),
|
||||
},
|
||||
// 代理页面
|
||||
@@ -67,33 +70,41 @@ const protectedRoutes: RouteObject[] = [
|
||||
{
|
||||
path: "",
|
||||
element: (
|
||||
<Suspense fallback={<div>加载中...</div>}>
|
||||
<AgentsList />
|
||||
</Suspense>
|
||||
<ProtectedRoute>
|
||||
<Suspense fallback={<div>加载中...</div>}>
|
||||
<AgentsList />
|
||||
</Suspense>
|
||||
</ProtectedRoute>
|
||||
),
|
||||
},
|
||||
{
|
||||
path: ":id",
|
||||
element: (
|
||||
<Suspense fallback={<div>加载中...</div>}>
|
||||
<AgentDetail />
|
||||
</Suspense>
|
||||
<ProtectedRoute>
|
||||
<Suspense fallback={<div>加载中...</div>}>
|
||||
<AgentDetail />
|
||||
</Suspense>
|
||||
</ProtectedRoute>
|
||||
),
|
||||
},
|
||||
{
|
||||
path: "create",
|
||||
element: (
|
||||
<Suspense fallback={<div>加载中...</div>}>
|
||||
<CreateAgent />
|
||||
</Suspense>
|
||||
<ProtectedRoute>
|
||||
<Suspense fallback={<div>加载中...</div>}>
|
||||
<CreateAgent />
|
||||
</Suspense>
|
||||
</ProtectedRoute>
|
||||
),
|
||||
},
|
||||
{
|
||||
path: "edit/:id",
|
||||
element: (
|
||||
<Suspense fallback={<div>加载中...</div>}>
|
||||
<EditAgent />
|
||||
</Suspense>
|
||||
<ProtectedRoute>
|
||||
<Suspense fallback={<div>加载中...</div>}>
|
||||
<EditAgent />
|
||||
</Suspense>
|
||||
</ProtectedRoute>
|
||||
),
|
||||
},
|
||||
],
|
||||
@@ -113,9 +124,11 @@ const protectedRoutes: RouteObject[] = [
|
||||
{
|
||||
path: "config",
|
||||
element: (
|
||||
<Suspense fallback={<div>加载中...</div>}>
|
||||
<StatusPageConfig />
|
||||
</Suspense>
|
||||
<ProtectedRoute>
|
||||
<Suspense fallback={<div>加载中...</div>}>
|
||||
<StatusPageConfig />
|
||||
</Suspense>
|
||||
</ProtectedRoute>
|
||||
),
|
||||
},
|
||||
],
|
||||
@@ -127,33 +140,41 @@ const protectedRoutes: RouteObject[] = [
|
||||
{
|
||||
path: "",
|
||||
element: (
|
||||
<Suspense fallback={<div>加载中...</div>}>
|
||||
<MonitorsList />
|
||||
</Suspense>
|
||||
<ProtectedRoute>
|
||||
<Suspense fallback={<div>加载中...</div>}>
|
||||
<MonitorsList />
|
||||
</Suspense>
|
||||
</ProtectedRoute>
|
||||
),
|
||||
},
|
||||
{
|
||||
path: ":id",
|
||||
element: (
|
||||
<Suspense fallback={<div>加载中...</div>}>
|
||||
<MonitorDetail />
|
||||
</Suspense>
|
||||
<ProtectedRoute>
|
||||
<Suspense fallback={<div>加载中...</div>}>
|
||||
<MonitorDetail />
|
||||
</Suspense>
|
||||
</ProtectedRoute>
|
||||
),
|
||||
},
|
||||
{
|
||||
path: "create",
|
||||
element: (
|
||||
<Suspense fallback={<div>加载中...</div>}>
|
||||
<CreateMonitor />
|
||||
</Suspense>
|
||||
<ProtectedRoute>
|
||||
<Suspense fallback={<div>加载中...</div>}>
|
||||
<CreateMonitor />
|
||||
</Suspense>
|
||||
</ProtectedRoute>
|
||||
),
|
||||
},
|
||||
{
|
||||
path: "edit/:id",
|
||||
element: (
|
||||
<Suspense fallback={<div>加载中...</div>}>
|
||||
<EditMonitor />
|
||||
</Suspense>
|
||||
<ProtectedRoute>
|
||||
<Suspense fallback={<div>加载中...</div>}>
|
||||
<EditMonitor />
|
||||
</Suspense>
|
||||
</ProtectedRoute>
|
||||
),
|
||||
},
|
||||
],
|
||||
@@ -162,9 +183,11 @@ const protectedRoutes: RouteObject[] = [
|
||||
{
|
||||
path: "/notifications",
|
||||
element: (
|
||||
<Suspense fallback={<div>加载中...</div>}>
|
||||
<NotificationsConfig />
|
||||
</Suspense>
|
||||
<ProtectedRoute>
|
||||
<Suspense fallback={<div>加载中...</div>}>
|
||||
<NotificationsConfig />
|
||||
</Suspense>
|
||||
</ProtectedRoute>
|
||||
),
|
||||
},
|
||||
// 用户管理
|
||||
@@ -174,25 +197,31 @@ const protectedRoutes: RouteObject[] = [
|
||||
{
|
||||
path: "",
|
||||
element: (
|
||||
<Suspense fallback={<div>加载中...</div>}>
|
||||
<UsersList />
|
||||
</Suspense>
|
||||
<ProtectedRoute>
|
||||
<Suspense fallback={<div>加载中...</div>}>
|
||||
<UsersList />
|
||||
</Suspense>
|
||||
</ProtectedRoute>
|
||||
),
|
||||
},
|
||||
{
|
||||
path: "create",
|
||||
element: (
|
||||
<Suspense fallback={<div>加载中...</div>}>
|
||||
<CreateUser />
|
||||
</Suspense>
|
||||
<ProtectedRoute>
|
||||
<Suspense fallback={<div>加载中...</div>}>
|
||||
<CreateUser />
|
||||
</Suspense>
|
||||
</ProtectedRoute>
|
||||
),
|
||||
},
|
||||
{
|
||||
path: ":id",
|
||||
element: (
|
||||
<Suspense fallback={<div>加载中...</div>}>
|
||||
<EditUser />
|
||||
</Suspense>
|
||||
<ProtectedRoute>
|
||||
<Suspense fallback={<div>加载中...</div>}>
|
||||
<EditUser />
|
||||
</Suspense>
|
||||
</ProtectedRoute>
|
||||
),
|
||||
},
|
||||
],
|
||||
@@ -202,9 +231,11 @@ const protectedRoutes: RouteObject[] = [
|
||||
{
|
||||
path: "/profile",
|
||||
element: (
|
||||
<Suspense fallback={<div>加载中...</div>}>
|
||||
<UserProfile />
|
||||
</Suspense>
|
||||
<ProtectedRoute>
|
||||
<Suspense fallback={<div>加载中...</div>}>
|
||||
<UserProfile />
|
||||
</Suspense>
|
||||
</ProtectedRoute>
|
||||
),
|
||||
},
|
||||
];
|
||||
@@ -246,7 +277,7 @@ const publicRoutes: RouteObject[] = [
|
||||
];
|
||||
|
||||
// 创建路由
|
||||
const router = createBrowserRouter(
|
||||
const router: ReturnType<typeof createBrowserRouter> = createBrowserRouter(
|
||||
[
|
||||
{
|
||||
element: (
|
||||
@@ -268,4 +299,4 @@ const router = createBrowserRouter(
|
||||
}
|
||||
);
|
||||
|
||||
export default router;
|
||||
export default router;
|
||||
Reference in new issue
Block a user