面试官您好,我曾在实际项目中搭建过完整的用户系统,下面我从 登录流程、Token 生成、角色权限控制 三方面来讲解。
用户输入账号密码,前端发起 /login
请求
后端验证用户名、密码(bcrypt 加密比对)
验证通过后生成 JWT,返回给前端
const token = jwt.sign({ userId, role }, secret, { expiresIn: '2h' });
后端接口使用中间件校验:
function authMiddleware(req, res, next) {
const token = req.headers.authorization?.split(' ')[1];
if (!token) return res.status(401).send('Unauthorized');
try {
const user = jwt.verify(token, secret);
req.user = user;
next();
} catch (err) {
return res.status(403).send('Token Invalid');
}
}
通过装饰器或中间件判断:
function roleGuard(allowedRoles: string[]) {
return (req, res, next) => {
if (!allowedRoles.includes(req.user.role)) return res.status(403).send('Forbidden');
next();
};
}
router.get('/admin/dashboard', authMiddleware, roleGuard(['admin']), handler);
我实现过一个基于 NestJS + TypeORM + MySQL 的博客系统,具备完整的 CRUD 和评论功能,还结合了用户权限管理。
User(id, username, role)
Post(id, title, content, userId)
Comment(id, content, postId, userId)
@Post('/post')
@UseGuards(JwtAuthGuard, RolesGuard('admin', 'editor'))
createPost(@Body() dto: CreatePostDto) {
return this.postService.create(dto);
}
@Post('/post/:id/comment')
@UseGuards(JwtAuthGuard)
addComment(@Param('id') postId, @Body() dto) {
return this.commentService.add(postId, req.user.id, dto);
}
admin:全权
editor:发文
user:评论、点赞
权限控制通过守卫/中间件实现
这个场景在我们上传 100MB+ 视频、压缩包等资源时经常会遇到,我用过 Node.js 结合前端分片上传方案处理。
@Post('/upload/chunk')
uploadChunk(@UploadedFile() file, @Body() { fileHash, index }) {
const chunkPath = `${uploadDir}/${fileHash}/${index}`;
fs.writeFileSync(chunkPath, file.buffer);
}
@Get('/upload/check')
checkFile(@Query() { fileHash }) {
return fs.existsSync(`${uploadDir}/${fileHash}.merged`);
}
@Post('/upload/merge')
async merge(@Body() { fileHash, total }) {
const chunks = [...Array(total).keys()].map(i => fs.readFileSync(`${fileHash}/${i}`));
const filePath = `${uploadDir}/${fileHash}.merged`;
fs.writeFileSync(filePath, Buffer.concat(chunks));
}
为了防止某些接口被恶意调用,我写过一个简单的基于内存缓存或 Redis 的限流中间件。
const ipCache = new Map();
function rateLimiter(req, res, next) {
const ip = req.ip;
const now = Date.now();
const limit = 10;
const windowMs = 60 * 1000;
if (!ipCache.has(ip)) {
ipCache.set(ip, []);
}
const timestamps = ipCache.get(ip).filter(t => now - t < windowMs);
if (timestamps.length >= limit) {
return res.status(429).send('Too many requests');
}
timestamps.push(now);
ipCache.set(ip, timestamps);
next();
}
如果是分布式部署,建议将缓存迁移到 Redis:
redis.incr(`rate:${ip}`)
redis.expire(`rate:${ip}`, 60)
我使用
node-cron
和nodemailer
实现过日报定时任务,部署在服务器长期运行。
import cron from 'node-cron';
import nodemailer from 'nodemailer';
cron.schedule('0 9 * * *', async () => {
const transporter = nodemailer.createTransport({ /* SMTP 配置 */ });
const users = await getAllUsers(); // 查询收件人
for (const user of users) {
await transporter.sendMail({
from: '"日报系统" ',
to: user.email,
subject: '你的日报',
html: '今日数据...
',
});
}
console.log('日报发送完毕');
});
支持任务日志、失败重试
可结合数据库记录发送状态,防止重复发送
编号 | 场景题 | 核心设计与实现 |
---|---|---|
8.1 | 登录 + JWT + 权限系统 | JWT 中间件 + 角色守卫 + bcrypt 加密 |
8.2 | 博客系统(文章+评论+权限) | NestJS 模块 + TypeORM 关系 + RBAC |
8.3 | 大文件上传 + 秒传 + 合并 | 分片上传接口 + Hash 判断 + Buffer 合并 |
8.4 | 接口限流 | 基于 IP 的时间窗口限流策略,内存/Redis |
8.5 | 定时任务系统(日报) | node-cron + nodemailer,每天定时群发邮件 |