2. 鉴权与授权基础:认证(Authentication)与授权(Authorization)的区别、OAuth2.0流程、JWT令牌结构

在构建多租户SaaS系统时,鉴权与授权是安全架构的基石。本章将深入剖析认证与授权的本质区别、OAuth2.0授权码流程的完整链路,以及JWT令牌的结构与验证机制。理解这些基础概念,是后续实现鉴权拦截链路异常捕获的前提。

2.1 认证(Authentication)与授权(Authorization)的区别

认证与授权是安全领域中两个极易混淆但职责完全不同的概念。认证解决“你是谁”的问题,授权解决“你能做什么”的问题。下表从多个维度进行对比:

维度 认证(Authentication) 授权(Authorization)
核心问题 你是谁? 你能做什么?
执行顺序 先执行,是授权的前提 后执行,依赖认证结果
传递信息 用户凭证(密码、令牌、生物特征) 权限范围(角色、资源、操作)
常见协议/机制 Basic Auth、Session-Cookie、JWT、OAuth2.0(获取令牌阶段) RBAC、ABAC、OAuth2.0 Scopes、ACL
失败结果 401 Unauthorized(实际应为未认证) 403 Forbidden
SaaS多租户场景 验证用户身份,并确定其所属租户 验证用户对特定租户资源的操作权限
常见误区:HTTP 401状态码在语义上应表示“未认证”,而非“未授权”。许多开发者误将权限不足返回401,正确的做法是返回403 Forbidden。在SaaS系统中,区分这两个状态码对异常链路排查至关重要。

2.2 OAuth2.0 授权码流程

OAuth2.0是目前业界最广泛使用的授权框架,它允许第三方应用获取对用户资源的有限访问权限,而无需暴露用户密码。在多租户SaaS架构中,OAuth2.0常用于实现跨租户的API访问委托。以下是最安全的授权码模式(Authorization Code Flow)完整流程:

  1. 用户访问第三方应用:用户通过浏览器访问客户端应用,触发登录/授权请求。
  2. 客户端发起授权请求:客户端将用户重定向到授权服务器(Authorization Server),携带 client_id、redirect_uri、response_type=code 及 scope 参数。
  3. 用户认证并授权:用户在授权服务器上登录,并确认授权范围(如:读取用户信息、写入文件等)。
  4. 授权服务器返回授权码:授权服务器通过重定向将授权码(Authorization Code)发送到客户端指定的 redirect_uri。
  5. 客户端换取令牌:客户端使用授权码、client_id、client_secret 在后端直接向授权服务器请求访问令牌(Access Token)和刷新令牌(Refresh Token)。
  6. 授权服务器返回令牌:授权服务器验证授权码和客户端凭证后,返回 access_token、refresh_token 及 expires_in。
  7. 客户端访问资源服务器:客户端使用 access_token 在请求头中携带 Authorization: Bearer <token> 访问资源服务器(Resource Server)的受保护API。
  8. 资源服务器验证令牌:资源服务器验证令牌的有效性(签名、过期时间、scope),返回资源数据。
SaaS多租户关键点:在OAuth2.0流程中,授权服务器需要识别用户所属的租户,并在生成的令牌中嵌入租户ID(tenant_id)和角色信息。资源服务器在验证令牌时,必须提取租户ID并执行租户隔离策略。
OAuth2.0 授权码流程 OAuth2.0 授权码流程 用户 (User) 客户端 (App) 授权服务器 资源服务器 流程步骤: ① 用户访问客户端 → ② 客户端重定向到授权服务器(client_id, redirect_uri, scope) ③ 用户认证并授权 → ④ 授权服务器返回授权码(code)到客户端 redirect_uri ⑤ 客户端用授权码 + client_secret 换取令牌 → ⑥ 授权服务器返回 access_token + refresh_token ⑦ 客户端用 access_token 请求资源服务器 → ⑧ 资源服务器验证令牌,返回资源 SaaS 扩展:令牌中嵌入 tenant_id、role、scope,资源服务器执行租户隔离。

2.3 JWT 令牌结构

JSON Web Token(JWT)是一种紧凑的、URL安全的令牌格式,常用于在OAuth2.0流程中传递访问令牌。JWT由三部分组成,使用Base64URL编码,并通过数字签名保证完整性。其结构如下:

部分 名称 内容 示例
Header 头部 令牌类型(typ)和签名算法(alg),如 {"alg":"RS256","typ":"JWT"} eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9
Payload 载荷 声明(Claims),包括标准声明(iss、sub、exp、iat)和自定义声明(tenant_id、role) eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwidGVuYW50X2lkIjoidGVuYW50LTAwMSJ9
Signature 签名 对Header和Payload的签名,防止篡改。使用Header中指定的算法和密钥生成 Ew5Q... (Base64URL编码的签名值)

一个完整的JWT字符串格式为:Header.Payload.Signature。以下是一个解码后的JWT Payload示例,包含SaaS多租户场景下的自定义声明:

{
  "sub": "user-12345",
  "iss": "https://auth.saas-platform.com",
  "iat": 1710000000,
  "exp": 1710003600,
  "tenant_id": "tenant-001",
  "role": "admin",
  "scope": ["read:orders", "write:orders"],
  "tenant_features": ["advanced-analytics", "audit-log"]
}
安全警告:JWT的Payload是Base64URL编码,并非加密。任何拥有令牌的人都可以解码查看Payload内容。切勿在JWT中存储密码、密钥等敏感信息。如需保护内容,应使用JWE(JSON Web Encryption)进行加密。
JWT 令牌结构分解 JWT 令牌结构分解 Header alg: RS256 typ: JWT Base64URL编码 . Payload sub: user-12345 tenant_id: tenant-001 Base64URL编码 . Signature HMACSHA256( base64UrlEncode(header) + "." + base64UrlEncode(payload), secret) 验证流程: 1. 资源服务器从 Authorization Header 提取 JWT。 2. 使用公钥/密钥对 Header + Payload 重新计算签名,与 Signature 比对。 3. 验证 exp(过期时间)、iss(签发者)、tenant_id(租户隔离)等声明。

2.4 多租户SaaS中的令牌设计要点

在多租户SaaS架构中,JWT令牌的设计需要额外考虑租户隔离和权限控制。以下是关键设计原则:

  • 嵌入租户上下文:在JWT Payload中必须包含 tenant_id 字段,资源服务器据此路由到对应的租户数据源。
  • 角色与权限分离:使用 role 字段标识用户角色(如 admin、editor、viewer),使用 scope 字段定义细粒度权限(如 read:orders、write:invoices)。
  • 令牌有效期管理:访问令牌(Access Token)有效期通常较短(15分钟-1小时),刷新令牌(Refresh Token)有效期较长(7-30天)。在SaaS中,租户管理员撤销用户权限时,需要配合令牌黑名单或短有效期实现快速失效。
  • 签名算法选择:生产环境推荐使用非对称算法(RS256、ES256),授权服务器持有私钥签名,资源服务器使用公钥验证,避免密钥分发风险。
  • 避免令牌膨胀:JWT会随请求头传输,过大的Payload会影响性能。只存放必要的声明,租户级别的配置信息应通过API查询,而非嵌入令牌。