代码评审 & 安全修复

后端:
- 创建 AdminGuard 替代 12 处手动 role 查库检查,统一用 JWT payload 中的 role
- 密码字段 select: false,所有需密码的查询显式 select('+password')
- 文件上传接口移除 @Public(),需 JWT 认证
- 管理员搜索关键词限长 50 字符防 ReDoS
- CORS 收窄,不再对非生产环境放行所有源
- postbuild 复制 certs 路径同步到 dist/src/certs
- package.json main/start:prod 路径更新为 dist/src/main

前端:
- resume.vue 文件上传补充 Authorization header
- login.vue 移除含用户邮箱的 console.log 日志
This commit is contained in:
yuzhiran
2026-06-11 19:55:10 +08:00
parent f7da843d56
commit 6dfb6bef48
14 changed files with 61 additions and 104 deletions
+4 -4
View File
@@ -108,7 +108,7 @@ export class UserService {
emailCodeStore.delete(email)
// 按邮箱查找或创建用户
let user = await this.userModel.findOne({ email }).exec()
let user = await this.userModel.findOne({ email }).select('+password').exec()
let isNew = false
if (!user) {
isNew = true
@@ -120,7 +120,7 @@ export class UserService {
// 🔐 密码登录
async loginByPassword(email: string, password: string) {
const user = await this.userModel.findOne({ email }).exec()
const user = await this.userModel.findOne({ email }).select('+password').exec()
if (!user) throw new HttpException('账号不存在', HttpStatus.NOT_FOUND)
if (!user.password) throw new HttpException('该账号未设置密码,请使用验证码登录', HttpStatus.UNAUTHORIZED)
const match = await bcrypt.compare(password, user.password)
@@ -136,7 +136,7 @@ export class UserService {
if (!password || password.length < 6) {
throw new HttpException('密码至少6位', HttpStatus.BAD_REQUEST)
}
const existing = await this.userModel.findOne({ email }).exec()
const existing = await this.userModel.findOne({ email }).select('+password').exec()
if (existing) {
if (existing.password) {
throw new HttpException('该邮箱已注册,请直接登录', HttpStatus.CONFLICT)
@@ -192,7 +192,7 @@ export class UserService {
}
private generateAuthResponse(user: UserDocument) {
const payload = { userId: user._id.toString(), phone: user.phone || '' }
const payload = { userId: user._id.toString(), phone: user.phone || '', role: user.role || 'user' }
return {
token: this.jwtService.sign(payload),
user: this.safeUser(user),