49dbced0cb
- 更新 logo.svg/logo-light.svg/logo-white.svg 品牌标识 - 新增 logo-calligraphy.svg 书法体 Logo 变体 - 新增新闻、二维码、微信业务等图片资源 - 删除旧版 JPEG 测试图片 - 新增 CMS 数据层服务模块 (admin-api, auth, cms, crypto, db) - 新增 cases/cross-references/methodology/team 常量数据 - 新增 site-config 站点配置
84 lines
2.5 KiB
TypeScript
84 lines
2.5 KiB
TypeScript
import jwt from 'jsonwebtoken';
|
|
import bcrypt from 'bcryptjs';
|
|
import { type NextRequest } from 'next/server';
|
|
|
|
const JWT_SECRET = process.env.JWT_SECRET || 'novalon-cms-secret-key-2024';
|
|
const JWT_REFRESH_SECRET = process.env.JWT_REFRESH_SECRET || 'novalon-cms-refresh-secret-2024';
|
|
const TOKEN_EXPIRY = '24h';
|
|
const REFRESH_TOKEN_EXPIRY = '7d';
|
|
|
|
export interface JwtPayload {
|
|
userId: string;
|
|
username: string;
|
|
role: string;
|
|
}
|
|
|
|
// ============ 密码工具 ============
|
|
|
|
export async function hashPassword(password: string): Promise<string> {
|
|
return bcrypt.hash(password, 10);
|
|
}
|
|
|
|
export async function verifyPassword(password: string, hash: string): Promise<boolean> {
|
|
return bcrypt.compare(password, hash);
|
|
}
|
|
|
|
// ============ Token 工具 ============
|
|
|
|
export function generateTokens(payload: JwtPayload): { accessToken: string; refreshToken: string } {
|
|
const accessToken = jwt.sign(payload, JWT_SECRET, { expiresIn: TOKEN_EXPIRY });
|
|
const refreshToken = jwt.sign(payload, JWT_REFRESH_SECRET, { expiresIn: REFRESH_TOKEN_EXPIRY });
|
|
return { accessToken, refreshToken };
|
|
}
|
|
|
|
export function verifyAccessToken(token: string): JwtPayload {
|
|
return jwt.verify(token, JWT_SECRET) as JwtPayload;
|
|
}
|
|
|
|
export function verifyRefreshToken(token: string): JwtPayload {
|
|
return jwt.verify(token, JWT_REFRESH_SECRET) as JwtPayload;
|
|
}
|
|
|
|
// ============ 请求认证 ============
|
|
|
|
export function getTokenFromRequest(request: NextRequest): string | null {
|
|
const authHeader = request.headers.get('authorization');
|
|
if (authHeader?.startsWith('Bearer ')) {
|
|
return authHeader.slice(7);
|
|
}
|
|
return null;
|
|
}
|
|
|
|
export function authenticateRequest(request: NextRequest): JwtPayload | null {
|
|
const token = getTokenFromRequest(request);
|
|
if (!token) return null;
|
|
try {
|
|
return verifyAccessToken(token);
|
|
} catch {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
// ============ Cookie 工具 ============
|
|
|
|
export function setTokenCookie(response: Response, accessToken: string, refreshToken: string): void {
|
|
response.headers.set(
|
|
'Set-Cookie',
|
|
`novalon_token=${accessToken}; Path=/; HttpOnly; SameSite=Lax; Max-Age=86400`
|
|
);
|
|
response.headers.append(
|
|
'Set-Cookie',
|
|
`novalon_refresh=${refreshToken}; Path=/; HttpOnly; SameSite=Lax; Max-Age=604800`
|
|
);
|
|
}
|
|
|
|
export function clearTokenCookie(response: Response): void {
|
|
response.headers.set(
|
|
'Set-Cookie',
|
|
`novalon_token=; Path=/; HttpOnly; SameSite=Lax; Max-Age=0`
|
|
);
|
|
response.headers.append(
|
|
'Set-Cookie',
|
|
`novalon_refresh=; Path=/; HttpOnly; SameSite=Lax; Max-Age=0`
|
|
);
|
|
} |