Token设计与选型


Token设计

Token是一个令牌,客户端访问服务器时,验证通过后服务端会为其签发一张令牌,之后客户端就可以携带令牌访问服务器,服务端只需要验证令牌的有效性即可。

一句话概括;访问资源接口(API)时所需要的资源凭证

在身份验证和授权领域,”有状态”(stateful)和”无状态”(stateless)通常用来描述系统处理用户认证信息的方式。

有状态(Stateful):

有状态的认证系统在服务器端会维护用户的会话状态信息。这意味着服务器端会存储关于用户认证状态的信息,比如用户的登录状态、会话信息、权限等。
在有状态的认证系统中,服务器端在处理用户请求时,会根据存储的会话状态来验证用户的身份和权限,通常会使用会话标识(如 Session ID)来识别用户的会话状态。
典型的例子包括传统的基于会话的认证方式,用户在登录后会获得一个会话标识(Session ID),服务器端会在会话管理中保持用户的登录状态,并在后续请求中使用该会话标识来验证用户的身份。

使用有状态令牌(Stateful Token)在特定场景中具有显著优势,特别是在需要服务器端管理会话状态和即时控制会话生命周期的情况下。以下是一些适合使用有状态令牌的场景:

1. 高度安全性要求的应用

  • 即时会话撤销:在高安全性应用中,例如金融服务、在线支付和医疗系统,可能需要立即撤销某些会话。服务器端存储会话状态,可以在需要时立即无效化令牌。
  • 敏感操作:对于涉及敏感数据和操作的系统,服务器端管理可以提供额外的控制和审计能力。

2. 单一服务器或共享存储的系统

  • 集中式会话管理:在单服务器应用或使用集中式共享存储的环境中,服务器端存储和管理会话状态较为简单和高效。
  • 小规模系统:对于小规模系统或负载较低的系统,管理会话状态的开销相对较低。

3. 需要复杂的会话控制,需长时间会话管理的应用

  • 细粒度权限控制:服务器端可以根据用户会话状态进行细粒度的权限控制,例如临时提升权限、基于活动时间段的权限控制等。
  • 动态会话属性:在会话期间可能需要动态调整用户的会话属性(如角色、权限等),服务器端管理更加灵活。
  • 长期会话:在需要长时间保持用户会话的应用中,例如企业内部应用或需要频繁交互的应用,服务器端可以更好地管理和维护会话状态。
  • 会话恢复:支持会话恢复,用户可以在不同设备和会话中无缝切换。

4. 严格的会话一致性需求,不需要跨域

  • 强一致性:需要确保会话状态的一致性,如电商购物车、多人协作工具等,服务器端可以更好地保证一致性和数据同步。
  • 内部系统:主要用于内部系统,不需要跨域、跨平台访问,这类系统更适合使用有状态令牌。

无状态(Stateless):

无状态的认证系统不会在服务器端维护用户的会话状态信息。每个请求都是独立的,服务器不会存储任何关于用户的会话状态。
在无状态的认证系统中,服务器端不需要存储任何会话状态,所有的认证信息都被包含在每个请求中,通常是在请求的头部(Header)或参数中。
典型的例子包括基于令牌(Token)的认证方式,比如 JSON Web Token(JWT)。在这种方式下,用户在登录后会获得一个令牌(Token),服务器端会使用令牌中的信息来验证用户的身份和权限,而不需要存储任何会话状态。

即使使用 JWT 进行用户认证,后端数据库仍然需要存储用户的账户和密码信息。这是因为:

  • 用户注册和登录:在用户注册时,需要存储他们的账户信息(如用户名、邮箱)和密码(通常是经过哈希处理的密码)。
  • 身份验证:在用户登录时,后端需要验证用户输入的密码是否正确,这需要与数据库中存储的密码进行比对。

JWT 是在用户成功登录后生成的令牌,通常包含用户的身份信息和权限信息。其生成和使用过程如下:

  • 用户登录:用户提交用户名和密码。
  • 身份验证:服务器验证用户名和密码。如果验证成功,服务器生成一个 JWT,包含用户的身份信息(如用户ID)和其他必要的声明(claims)。
  • 返回 JWT:服务器将生成的 JWT 返回给客户端。
  • 后续请求:客户端在后续请求中携带 JWT,服务器通过验证 JWT 的签名和有效期来确认用户身份。

JWT 在用户认证和授权方面具有以下优势:

  • 无状态性:服务器不需要存储用户的会话信息,每次请求都可以通过验证 JWT 来确认用户身份。

  • 扩展性:适用于分布式系统和微服务架构,因为令牌是自包含的,不需要共享会话状态。

  • 安全性:通过加密和签名,确保令牌的完整性和可信度。
    Cookie-Based Session

  • 优点:简单、安全性高(服务器端存储)。

  • 缺点:扩展性差,服务器压力大。

Token-Based Authentication

  • 优点:无状态、扩展性好。
  • 缺点:客户端存储安全性较差。

JWT

  • 优点:无状态、自包含、适合分布式系统。
  • 缺点:安全性依赖于客户端存储和令牌签名。

Cookie-Based Session:适用于简单的 Web 应用,特别是单服务器或小规模应用。

Token-Based Authentication:适用于需要跨多个客户端(如移动端、单页应用)的应用。

JWT:适用于分布式系统和微服务架构,提供更高的扩展性和灵活性。

如果是在一个只有帐号跟密码的世界上,我就必须要把我的 Google 帐号和密码告诉给这第三方服务行事历服务,它才有办法取得这些资讯。但如果我把帐号密码告诉了第三方,它可能就可以在暗地里窃取行事历以外的资讯,像是在 Gmail 里的机密资讯。这时候就出现了使用 Access Token 来解决这个问题的协议,OAuth 2.0。

在 OAuth 2.0 中,使用短期的 Access Token 有几个主要的优势和原因:

  1. 安全性: 短期的 Access Token 有效期较短,如果被盗或泄露,攻击者可利用的时间窗口较小,减少了被滥用的风险。相比之下,长期有效的 Token 更容易成为攻击者的攻击目标。
  2. 可控制资源的访问权限(最小权限原则
  3. 强制刷新: 通过设定较短的有效期,强制客户端定期刷新 Access Token。这有助于保持令牌的有效性和安全性,并且可以促使客户端与认证服务器交互,更新并重新授权访问权限。
  4. 可撤销性: 您可以随时撤销第三方服务的访问权限。

但同时也要权衡安全性和效率。 Access Token应该维持在较短有效期,过长不安全,过短也会影响用户体验,因为频繁去刷新带来没有必要的网络请求。可以参考我们常常在某些网站停止操作一段时间之后就会掉线,这个时间是Refresh Token的有效期,Access Token不应长过这个时间。

Refresh Token的有效期就是允许用户在多久时间内不用重新登录的时间,可以很长,视业务而定。我们在使用某些APP的时候,即使一个月没有开过也是登录状态的,这6y就是Refresh Token决定的。授权服务在接到Refresh Token的时候还要进一步做客户端的验证,尽可能排除盗用的情况。

A refresh token can help you balance security with usability. Since refresh tokens are typically longer-lived, you can use them to request new access tokens after the shorter-lived access tokens expire.

It’s a common practice for some JWT frameworks or authentication systems to use a combination of JWT access tokens and opaque refresh tokens. This hybrid approach aims to leverage the advantages of both token types while mitigating their respective drawbacks.

通过引入 refresh token,系统设计者实现了一种混合模型:保持了大部分请求的无状态性(通过 JWT),同时通过 refresh token 实现必要的有状态管理(如 token 的更新和撤销)。这种方法有效地在用户体验、安全性和系统性能之间取得了平衡。
你的总结确实很好地解释了为什么在现代架构中,尽管追求无状态(stateless)
,但还是需要一些有状态(stateful)元素来解决实际问题。这样的设计既保持了分布式系统的优势,又提供了必要的安全控制和用户体验优化。

所有token应该保管在private的地方,也就是只能客户端自己使用,所有token都应该在TLS信道下发送(比如HTTPS)。

In an SSO/Auth system, the token you design should store information essential for secure and efficient authentication and authorization. Here’s a breakdown of the key information that should be included in your token:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
v.Set("scope", "iauth_token")
v.Set("response_type", "code")
v.Set("client_id", testService.ServiceID)
v.Set("state", uuid.NewString())
v.Set("redirect_uri", "https://test.hotstar.com:9999/callback")
tokenClaims := &iauth.TokenClaims{}
parser := jwt.Parser{}
_, _, err = parser.ParseUnverified(result.AccessToken, tokenClaims)
assert.Nil(t, err)
assert.Equal(t, tokenClaims.SubjectID, testUserId)
assert.Equal(t, tokenClaims.ExpiresAt, int64(result.ExpiresAt))
assert.Equal(t, tokenClaims.ExpiresAt-12*3600-2, tokenClaims.IssuedAt) //we set 2 second offset in issueAt
assert.True(t, tokenClaims.IssuedAt == tokenClaims.NotBefore)
assert.NotNil(t, tokenClaims.Id)

Essential Information:

  • User Identifier (UID): A unique identifier for the user, typically a numerical ID. This is crucial for identifying the user associated with the token.
  • Issuer (iss): Identifies the system that issued the token. This helps verify the token’s origin and prevent forgery.
  • Audience (aud): Specifies the intended recipient or service for the token. This ensures the token is used only by the intended service.
  • Expiration Time (exp): A timestamp indicating when the token expires. This limits the token’s validity and reduces the risk of unauthorized access if compromised.
  • Issued At Time (iat): A timestamp indicating when the token was issued. This helps track token age and detect potential issues.
  • Token Type (typ): Specifies the type of token being used (e.g., JWT, opaque token). This helps the receiving service correctly handle the token.

Optional but Recommended Information:

  • Scope (scp): Defines the permissions granted to the token holder. This allows for fine-grained control over what resources the user can access.
  • Nonce (jti): A unique identifier for the token, preventing replay attacks where a stolen token is used multiple times.
  • Session ID: A unique identifier for the user’s session. This allows for session management and revocation.
  • User Roles/Groups: Information about the user’s roles or groups within the system. This can be used for authorization decisions.
  • Refresh Token ID: If using a refresh token strategy, this identifies the associated refresh token for seamless token renewal.

Considerations:

  • Security: Prioritize security by using strong encryption algorithms and proper key management for token generation and validation.
  • Performance: Balance the amount of information stored in the token with performance considerations. Larger tokens can impact performance.
  • Privacy: Only include necessary information to minimize the risk of exposing sensitive user data.
  • Token Format: Choose a suitable token format like JWT (JSON Web Token) for its standardized structure and ease of parsing.

Author: Stan ke
Reprint policy: All articles in this blog are used except for special statements CC BY 4.0 reprint policy. If reproduced, please indicate source Stan ke !
  TOC