L.Leverage Learn从第一行代码,到读懂真实后端查看项目源码

NestJS 后端开发 / 第 15 课

NestJS 身份认证与资源权限

区分“你是谁”和“你能访问什么”,认识密码哈希、JWT、Guard 与私有提交的访问边界。

◷ 预计 25 分钟 · 面向初学者

登录系统要回答两个不同问题:认证回答“请求是谁发出的”,授权回答“这个身份能不能做这件事”。拿到有效 JWT 只能证明身份信息通过验证,并不代表用户可以读取所有提交、修改所有题目或进入所有竞赛。

注册时不应把原始密码写入数据库。应用用慢速密码哈希算法计算摘要,保存摘要而非明文;登录时用用户输入与保存的摘要进行验证。哈希不是可解密的加密,数据库泄露时也不能让用户继续使用相同密码。JWT 则是一种签名令牌,服务端校验签名与有效期后读取其中的用户标识。令牌不应放入源码、公开日志或课程练习截图;真实密码和 token 也不要贴到聊天或公开仓库。

Guard 先确认身份

NestJS 的 Guard 在控制器方法执行之前决定是否继续。像 @UseGuards(JwtAuthGuard) 这样的装饰器,把 Guard 应用到路由或控制器;Passport 策略校验 Bearer token 后通常会把用户信息放到请求上下文。没有凭证、签名错误或 token 过期通常返回 401 Unauthorized。Guard 适合确认身份、角色等入口条件,不是所有资源权限的替代品。

下面是教学写法,findVisibleTo 表示需要实现的资源权限方法,不是引用仓库里现成的方法;读取参数时也应做转换。

@UseGuards(JwtAuthGuard)
@Get(':id')
findOne(@CurrentUser() user: JwtPayload, @Param('id', ParseIntPipe) id: number) {
  return this.submissionService.findVisibleTo(id, user.sub);
}

@CurrentUser() 是从已认证请求取用户的参数装饰器;findVisibleTo 应查询提交并核对 submission.userId,或检查明确允许的管理角色。若用户已登录但不属于这条私有提交,返回 403;为了避免泄露资源是否存在,有些接口会对未授权和不存在统一返回 404。选择哪种响应应服从产品的隐私策略,不能因为 URL 中的 ID 是数字就信任调用者有权访问。

OJ 中创建提交时,服务端应从认证用户取得 userId,而不是接受客户端正文里的 userId 决定归属。客户端可以改请求正文、路径编号和查询参数,所以资源查询必须把身份条件纳入判断,而不能只按提交 ID 查到记录后直接返回。匿名访客不能通过改 URL 查询别人的私有代码。公开提交列表、题目管理和重判操作也有各自权限规则:例如对照题目是否公开、用户是否是竞赛成员、当前身份是否有管理员角色。身份认证和资源授权分别检查,避免把“已登录”等同于“全部可读写”。权限测试应至少覆盖资源本人、其他普通用户和管理员几种身份,并断言未授权时私有代码没有出现在响应中。这样能检查返回状态,也能检查敏感内容没有泄露。

小练习

用户 A 登录成功后请求 /submissions/72,记录 72 属于用户 B 且标记为私有。JWT 有效是否足以返回代码?应该在哪里核对?

**答案:**不够。Guard 确认 A 的身份后,Service 或资源授权逻辑还须确认 72 是否对 A 可见;否则不返回私有代码,并按接口策略响应 403 或 404。创建记录的用户 ID 也应来自已认证身份。

固定版本源码

互动练习:区分身份认证与资源授权

GET /reports/42 已识别出登录用户 U。报告 42 属于另一位用户,且 U 没有管理员角色。要避免“已登录就能看”,应如何判定?

选择一个答案

✓
这一课,你已经能……

认证确认请求身份,授权按资源和操作判断权限;Guard 可保护路由,但每个私有资源仍须核对所有权或角色。