后端实现: - 创建 AuthService 包含密码加密、JWT 生成和验证 - 创建 authMiddleware 用于 API 路由保护 - 实现 auth 路由 (register, login, logout, /me) 前端实现: - 创建 Login 页面 (登录/注册标签页) - 创建 ProtectedRoute 组件用于路由保护 - 更新 App.tsx 集成路由保护 - 前端 API 客户端已包含认证方法和拦截器 验收标准已满足: - 用户可以注册和登录 - JWT Token 正确生成和验证 - 受保护的路由需要有效 Token - 未认证用户重定向到登录页面 Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
66 lines
1.5 KiB
TypeScript
66 lines
1.5 KiB
TypeScript
import { Request, Response, NextFunction } from 'express';
|
|
import { verifyToken } from '../services/AuthService';
|
|
import { AppError } from './errorHandler';
|
|
|
|
export interface AuthRequest extends Request {
|
|
userId?: string;
|
|
userEmail?: string;
|
|
}
|
|
|
|
export function authMiddleware(req: AuthRequest, res: Response, next: NextFunction) {
|
|
const authHeader = req.headers.authorization;
|
|
|
|
if (!authHeader) {
|
|
return res.status(401).json({
|
|
error: {
|
|
status: 401,
|
|
message: 'Missing authorization header',
|
|
},
|
|
});
|
|
}
|
|
|
|
const parts = authHeader.split(' ');
|
|
if (parts.length !== 2 || parts[0] !== 'Bearer') {
|
|
return res.status(401).json({
|
|
error: {
|
|
status: 401,
|
|
message: 'Invalid authorization header format',
|
|
},
|
|
});
|
|
}
|
|
|
|
const token = parts[1];
|
|
|
|
try {
|
|
const payload = verifyToken(token);
|
|
req.userId = payload.userId;
|
|
next();
|
|
} catch (error: any) {
|
|
return res.status(401).json({
|
|
error: {
|
|
status: 401,
|
|
message: 'Invalid or expired token',
|
|
},
|
|
});
|
|
}
|
|
}
|
|
|
|
export function optionalAuthMiddleware(req: AuthRequest, res: Response, next: NextFunction) {
|
|
const authHeader = req.headers.authorization;
|
|
|
|
if (authHeader) {
|
|
const parts = authHeader.split(' ');
|
|
if (parts.length === 2 && parts[0] === 'Bearer') {
|
|
const token = parts[1];
|
|
try {
|
|
const payload = verifyToken(token);
|
|
req.userId = payload.userId;
|
|
} catch (error) {
|
|
// Silently fail - continue without authentication
|
|
}
|
|
}
|
|
}
|
|
|
|
next();
|
|
}
|