# 登录鉴权
# 身份认证标准
在登录鉴权中,身份认证标准通常涉及多个方面,包括身份的验证、授权以及数据安全性。常见的身份认证标准有以下几种:
# 1. OAuth 2.0
- 概述:OAuth 2.0 是目前最常用的授权框架,允许用户授权第三方应用访问自己的资源,而无需直接向其提供用户名和密码。OAuth 2.0 是授权协议,不直接进行身份验证,但通常与 OpenID Connect 一起使用来提供身份验证功能。举个例子,你在手机上点击「使用微信登录」时都会使用此标准,并且系统会询问你是否同意与该应用共享你的头像、昵称等数据。
- 特点:
- 提供令牌(access token)进行身份验证。
- 支持多种授权模式(如授权码模式、密码模式、客户端凭证模式等)。
- 使用时通常需要配合 OpenID Connect 进行身份认证。
# 2. OpenID Connect (OIDC)
- 概述:OpenID Connect 是建立在 OAuth 2.0 之上的身份验证协议,允许客户端应用验证用户的身份,并获取基本的用户信息。OIDC 提供了一种标准化的方式来实现基于 OAuth 的身份认证。实质是 OAuth 2.0 的一个超集,他在 OAuth 2.0 之上提供了更多用户信息和获取权限和标准。
- 特点:
- 通过 ID Token 来验证用户身份,ID Token 是由身份提供者(Identity Provider, IdP)签发的 JWT(JSON Web Token)。
- 支持用户信息查询(UserInfo endpoint)。
- 广泛支持与社交登录(如 Google、Facebook、GitHub 等)集成。
# 3. SAML 2.0 (Security Assertion Markup Language)
- 概述:SAML 2.0 是一种基于 XML 的开放标准,用于身份验证和授权信息交换。AML 允许企业应用程序和内部、外部程序无缝连接。通常用于企业级单点登录(SSO)场景。
- 特点:
- 使用 XML 格式的断言(Assertion)传递身份和授权信息。
- 适用于企业内部应用或跨域登录。
- 多用于大型组织和传统企业环境。
# 4. JWT (JSON Web Token)
- 概述:JWT 是一种轻量级的身份验证标准,广泛用于无状态的应用程序。它通常作为 OAuth 2.0 或 OpenID Connect 的 token 类型,用于存储和传递身份验证信息。
- 特点:
- JWT 是一个基于 JSON 的自包含的令牌,包含了用户的身份信息和权限,且信息是经过加密签名的。
- 不依赖于服务器端存储,适合分布式系统。
- 可以存储在浏览器的 LocalStorage 或 Cookie 中,或者在请求头中传递。
# 5. LDAP (Lightweight Directory Access Protocol)
- 概述:LDAP 是一种用于访问和管理目录信息的协议,可理解为一个树型的用来存储用户和组织信息的数据库,常被用来做单点登录( SSO )和企业员工信息管理。
- 特点:
- 用于集中管理用户账户和权限。
- 常用于企业级身份认证系统(如 Active Directory)。
- 支持身份验证、查询和修改用户信息。
# 6. CAS (Central Authentication Service)
- 概述:集中式认证服务,是一种单点登录协议。它的目的是允许一个用户访问多个应用程序,而只需向认证服务器提供一次凭证(如用户名和密码)。这样用户不仅不需在登录 Web 应用程序时重复认证,而且这些应用程序也无法获得密码等敏感信息。
- 特点:
- 通过 CAS,所有用户身份认证都由一个中心服务器管理,简化了用户管理和认证流程。
- CAS 协议相对简单,许多现代应用和框架都提供了现成的 CAS 客户端库,便于集成到各种应用程序中。
- 由于 CAS 使用 HTTP 协议,能够跨越多个域名进行身份认证,因此非常适合有多个子系统或跨域需求的场景。
- OAuth 通常需要与 OpenID Connect 配合使用以提供身份认证,而 CAS 本身就支持身份认证。
# 7. Basic Authentication
- 概述:Basic Authentication 是最简单的 HTTP 身份认证方式。通过将用户名和密码以基本的 Base64 编码传递在请求头中。
- 特点:
- 简单且易于实现,但缺乏安全性,容易受到中间人攻击(MITM)。
- 需要配合 HTTPS 使用,以确保密码的安全传输。
# 8. Two-Factor Authentication (2FA)
- 概述:二次身份验证是通过两个不同的身份验证因素来增强安全性,通常是“知识因子”(密码)和“持有因子”(短信验证码、硬件令牌等)。
- 特点:
- 增加安全性,减少密码泄露的风险。
- 需要用户提供第二种认证方式(如短信验证码、TOTP等)。
# 9. FIDO (Fast Identity Online)
- 概述:FIDO 是一个开放标准,专注于提供无密码认证方案,广泛用于支持生物识别、硬件密钥(如 YubiKey)、智能卡等认证方式。
- 特点:
- 支持多种认证方式,包括指纹、面部识别、硬件密钥等。
- 安全性高,不依赖于传统密码。
- 适用于无密码认证的需求。
# 扫码登录原理
扫码登录本质上是请求登录方请求已登录方将登录凭证写入特定媒介的过程。这里的请求登录方为 Web 端,已登录方为 APP 端,登录凭证可以是用户信息,也可以是换取用户信息的凭证,而特定媒介是某一张二维码。
微信扫码登录是一种通过微信账号在 Web 或移动端应用中进行身份验证和授权的方式。该登录方式通过微信扫码生成二维码,用户扫描二维码后在微信客户端授权登录,完成身份验证。整个过程的核心在于用户在手机端通过微信客户端进行确认,授权并将信息返回给应用服务器,最终实现用户登录。
# 1. 生成二维码
在 Web 或移动应用中,用户选择通过微信扫码登录时,应用会请求微信的授权接口,生成一个 二维码(QR Code)。
二维码的内容是一个包含临时登录凭证(
auth_code
)和相关信息的 URL,通常是指向微信的授权服务器的请求地址。这个请求会包括以下内容:- 唯一标识符(
auth_code
):生成的唯一 token,用于标识该次登录请求。 - 二维码有效期:二维码通常有有效期,一般是 2-5 分钟,防止被恶意利用。
- 回调地址(
redirect_uri
):用户扫描二维码后,微信客户端会将用户授权信息返回到这个 URL 地址。
请求生成二维码的 URL 格式通常类似:
https://api.weixin.qq.com/sns/oauth2/qrconnect?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=snsapi_login&state=STATE
1其中:
- 唯一标识符(
appid:该应用在微信开放平台的唯一标识。
redirect_uri:用户授权后,微信会将用户信息重定向回这个 URL。
response_type=code:表示使用授权码流程。
scope=snsapi_login:表示应用请求的授权类型,
snsapi_login
是扫码登录的授权类型。state:防止 CSRF 攻击的随机字符串,用于验证请求的合法性。
生成二维码的响应会返回一个二维码的 URL,第三方应用将这个 URL 编码生成二维码并展示给用户。
# 2. 用户扫描二维码
- 用户打开微信客户端,扫描第三方应用页面展示的二维码。微信客户端通过读取二维码中的 URL,解析出授权码(auth_code)及其相关信息。
- 维码中的信息会告诉微信客户端当前登录请求的来源应用及其请求的权限范围。二维码中包含的信息会指引微信客户端向微信授权服务器发起请求,要求用户授权该应用的登录权限。
# 3. 微信客户端授权登录
用户扫描二维码后,微信客户端会显示一个授权页面,要求用户确认是否允许该应用使用微信账号登录。
如果用户同意授权,微信会将用户的信息(如微信账号、昵称等)通过授权代码(
code
)返回到应用提供的回调地址(redirect_uri
)。这时,微信服务器会向用户的微信账户发送推送消息,提示用户扫码登录请求来源的设备和应用,以及是否同意授权。
如果用户同意授权,微信服务器会通过 HTTP 重定向的方式,将一个临时授权码(
code
)返回到应用设置的redirect_uri
,例如:https://your-app.com/callback?code=AUTH_CODE&state=STATE // 其中 code 为临时授权码,state 用于防止 CSRF 攻击。
1
2
3
# 4. 第三方应用服务器获取 access_token
在应用的回调接口(
redirect_uri
)接收到授权码(code
)后,应用服务器使用该授权码向微信服务器请求 access_token(访问令牌)。这一步是通过调用微信开放平台的接口实现的:请求的 URL 格式如下:
POST https://api.weixin.qq.com/sns/oauth2/access_token
1请求参数包含:
- appid:应用的唯一标识。
- secret:应用的密钥。
- code:微信授权页面返回的授权码(
code
)。 - grant_type:固定为
authorization_code
,表示使用授权码流程。
如果授权码有效且请求成功,微信服务器会返回 access_token 和 openid,其中:
- access_token:用于访问用户信息的令牌,有效期为 2 小时,访问用户的微信数据。
- openid:用户在该应用下的唯一标识符,用于区分不同用户。
- expires_in:
access_token
的有效期(通常为 7200 秒,即 2 小时)。 - refresh_token:刷新 access_token 的令牌
access_token
是后续访问用户信息的关键凭证,应用服务器会将其存储并用于与微信服务器通信。
# 5. 应用服务器使用 access_token
获取用户信息
一旦应用服务器获得了
access_token
,它可以使用这个令牌来访问用户的基本信息,通常会调用微信的用户信息接口:GET https://api.weixin.qq.com/sns/userinfo
1请求参数包括:
- access_token:前面获得的访问令牌。
- openid:用户在该应用下的唯一标识。
微信服务器返回的用户信息包含:
- 用户的 openid、nickname、sex、city、province、country、headimgurl 等。
应用服务器使用这些用户信息来完成登录流程,例如创建用户会话、更新用户信息或记录登录历史等。
# 6. 完成登录并建立会话
- 一旦应用服务器获取了用户的
openid
和其他信息,它可以在应用内为用户创建会话。通常会使用 Cookie 或 Session 来维持登录状态。 - 用户登录成功后,应用会向客户端返回一个会话标识(如会话 ID、Token 等),并展示用户的个人信息或授权操作。
# 7. 二维码失效与过期
- 为了安全,二维码通常会有一个有效期,过期后二维码无法使用。此时用户需要重新生成二维码进行扫码操作。
微信扫码登录的安全性主要体现在以下几个方面:
二维码有效期短:
- 二维码的有效期通常很短,防止二维码被盗用或滥用。
防止 CSRF 攻击:
- 微信扫码登录过程中使用了
state
参数来防止跨站请求伪造(CSRF)攻击。state
参数是一个随机字符串,在请求和回调时保持一致,确保请求的合法性。
- 微信扫码登录过程中使用了
加密与验证:
- 微信的
access_token
和openid
是加密的,服务器与微信的通信采用 HTTPS,确保通信过程中的数据不被篡改。
- 微信的
登录授权的双向验证:
- 用户必须在微信客户端上确认授权,防止恶意程序直接使用扫码的二维码获取登录凭证。
二维码一次性使用:
- 一旦用户扫描并授权后,二维码即失效,不能再次使用,防止被滥用。
微信扫码授权登录依赖于微信开放平台提供的授权接口和二维码生成机制。在这个流程中,微信充当身份提供方,第三方应用作为客户端。具体来说,微信扫码授权登录的关键技术点在于生成二维码、获取授权码、通过授权码获取 access_token 和用户信息、最终完成会话管理和用户登录。
# 单点登录原理
单点登录的原理是通过一个中央认证服务(也叫身份认证服务器)来验证用户的身份,所有应用系统都将认证委托给该认证服务。每当用户访问一个需要认证的应用时,如果没有登录,会被引导到 SSO 认证服务器进行身份验证,认证通过后,认证服务器会生成一个令牌(Token),用户凭借该令牌在所有关联的应用中访问资源,而无需重新登录。
SSO 主要基于 身份提供者(Identity Provider,IdP) 和 服务提供者(Service Provider,SP) 的信任关系。身份提供者 负责身份认证,服务提供者 是用户访问的目标应用,服务提供者依赖身份提供者来完成用户认证。
实现 SSO 登录的标准协议的推荐度:OIDC > SAML2 > CAS 1.0 > LDAP > OAuth2.0。
# 同域 SSO - Session-Cookie
当存在两个相同域名下的系统 A a.abc.com
和系统 B b.abc.com
时,以下为他们实现 SSO 的步骤:
- 用户访问某个子系统时(例如
a.abc.com
),如果没有登录,则跳转至 SSO 认证中心提供的登录页面进行登录 - 登录认证后,服务端把登录用户的信息存储于 Session 中,并为用户生成对应的会话身份凭证附加在响应头的 Set-Cookie 字段中,随着请求返回写入浏览器中,并回跳到设定的子系统链接中
- 下次发送请求时,当用户访问同域名的系统 B 时,由于 A 和 B 在相同域名下,也是 abc.com,浏览器会自动带上之前的 Cookie。此时服务端就可以通过该 Cookie 来验证登录状态了。
这实际上使用的就是 Session-Cookie 认证的登录方式。
# 跨域 SSO
OpenID Connect(OIDC) 是一种基于 OAuth 2.0 的身份认证协议,专门用于实现用户身份验证和单点登录(SSO)。OIDC 通过提供一个标准化的身份认证流程,使得用户可以通过单一登录访问多个应用和服务。OIDC 扩展了 OAuth 2.0 的功能,除了授权之外,还提供了身份验证的功能,使得应用不仅可以验证用户的授权(如获取数据),还可以确认用户的身份。
OIDC 实现 SSO 的核心原理是通过 身份提供者(Identity Provider,IdP) 来对用户进行身份认证,并通过返回 ID Token 和 Access Token 的方式,允许服务提供者(Service Providers,SP)验证用户身份,从而实现跨应用的无缝登录。OIDC 基于 OAuth 2.0 的授权流程,采用了 JSON Web Token(JWT) 格式的令牌来传递身份信息。OIDC 的认证过程通常包括三个重要的令牌:
- ID Token:主要用于身份认证,通常是一个包含用户身份信息的 JWT。
- Access Token:用于访问资源,通常用于访问授权的 API。
- Refresh Token:用于刷新过期的 Access Token。
OIDC 的 SSO 实现通过这些令牌的交互来确保用户的身份验证和访问控制。
# 1. 用户访问服务提供者(SP)
用户首次访问服务提供者(SP),例如一个 Web 应用,且该应用要求进行身份验证。
- 如果用户尚未登录,SP 会将用户重定向到 身份提供者(IdP) 进行认证。
- 重定向过程中,SP 会向 IdP 发送一个认证请求,包含以下信息:
- client_id:服务提供者的唯一标识符。
- redirect_uri:认证完成后,用户将被重定向到的 URL(SP 提供)。
- response_type:要求返回的响应类型(例如,
code
用于授权码流程)。 - scope:请求的权限范围,通常包含
openid
,表示请求 OIDC 验证。 - state:防止 CSRF 攻击的随机字符串。
例如,重定向 URL 可能类似于:
https://idp.example.com/authorize?response_type=code&client_id=client-id&redirect_uri=https://sp.example.com/callback&scope=openid&state=xyz123
# 2. 用户在身份提供者(IdP)进行认证
用户在 IdP 的登录页面输入用户名和密码(或通过其他方式,如多因素认证、社交登录等)进行身份验证。
- 如果身份验证成功,IdP 会生成一个 授权码(Authorization Code),并将其通过 重定向(Redirect) 返回给服务提供者(SP)指定的
redirect_uri
。
例如,重定向到:
https://sp.example.com/callback?code=authorization_code&state=xyz123
# 3. 服务提供者(SP)请求 Access Token 和 ID Token
SP 接收到授权码后,会向 IdP 发起后端请求,交换授权码换取 Access Token 和 ID Token。
请求的内容通常包括:
- grant_type:通常为
authorization_code
。 - code:上一步获得的授权码。
- redirect_uri:与第一步中的
redirect_uri
保持一致。 - client_id 和 client_secret:SP 的标识符和密钥。
示例请求:
POST https://idp.example.com/token
Content-Type: application/x-www-form-urlencoded
grant_type=authorization_code&code=authorization_code&redirect_uri=https://sp.example.com/callback&client_id=client-id&client_secret=client-secret
2
3
4
# 4. 身份提供者返回 ID Token 和 Access Token
IdP 验证请求后,向 SP 返回 Access Token 和 ID Token。ID Token 是一个 JWT(JSON Web Token),包含了用户的身份信息(例如,用户的唯一标识符、登录时间、过期时间等)。
示例响应:
{
"access_token": "access_token_value",
"id_token": "id_token_value",
"expires_in": 3600,
"token_type": "Bearer"
}
2
3
4
5
6
- ID Token:包含用户的身份信息(如用户 ID、认证时间等),用于证明用户已通过身份验证。
- Access Token:用于访问受保护的资源或 API。它由服务提供者使用,用于在其他系统中执行授权操作。
# 5. 验证 ID Token 和 Access Token
SP 使用 ID Token 来验证用户的身份。由于 ID Token 是签名的 JWT,SP 可以通过验证签名来确保令牌的合法性,并且从中提取出用户的身份信息。
- 如果 ID Token 的签名有效,SP 会根据其中的用户信息创建一个会话,表示该用户已登录。
- SP 还可以使用 Access Token 访问与用户相关的资源,或者调用后端 API 进行数据请求。
# 6. 用户访问服务提供者(SP)资源
在用户成功认证后,SP 可以根据 ID Token 和 Access Token 来授权用户访问应用的资源。此时,用户在 SP 上的登录状态已经建立。
- 用户可以直接访问 SP 中的资源,而无需重新登录。
- 如果用户访问其他关联的服务提供者(如果该服务也支持 OIDC SSO),则 SP 可以使用现有的 ID Token 或通过 OAuth 2.0 的机制去请求相应的 Access Token,进行无缝的跨应用登录。
# 7. 跨服务提供者的 SSO
如果其他服务提供者(SP)也支持 OIDC,用户无需重新进行身份验证。SP 可以通过 ID Token 来验证用户身份,或者通过 Refresh Token 获取新的 Access Token 和 ID Token。
- Refresh Token:如果 Access Token 或 ID Token 过期,SP 可以通过 Refresh Token 向 IdP 请求新的令牌,而不需要用户再次登录。
OIDC 实现 SSO 的关键点:
- 身份认证:OIDC 扩展了 OAuth 2.0,提供了身份认证功能,用户通过身份提供者(IdP)完成身份验证。
- ID Token 和 Access Token:OIDC 使用 JWT 格式的 ID Token 来传递用户身份信息,并使用 Access Token 来授权访问资源。
- 无缝登录:通过 ID Token 和 Access Token,用户可以在多个服务提供者(SP)之间无缝切换,免去多次登录的麻烦。
- 跨平台支持:OIDC 支持 Web 应用、移动应用等多种平台,广泛应用于各种跨应用和跨域的 SSO 需求。
# 权限设计
权限设计 是一个应用系统中至关重要的部分,它决定了不同用户、角色和群体能够访问哪些资源、功能和数据。一个合理的权限设计可以确保系统的安全性和功能的细粒度控制,同时保证系统的扩展性与维护性。常见的权限设计模式包括基于角色的访问控制(RBAC)、基于属性的访问控制(ABAC)、基于权限的访问控制(PBAC)等。
# 权限设计的基本概念
- 用户(User):指使用系统的个体,可以是员工、客户、管理员等。
- 角色(Role):系统中定义的用户类别,每个角色代表一组权限。一个用户可以有多个角色。
- 权限(Permission):定义了用户可以执行的操作或访问的资源。例如,查看数据、编辑内容、删除记录等。
- 资源(Resource):系统中的各种数据或功能模块,例如文件、页面、接口、操作等。
- 操作(Action):用户能够在资源上执行的操作,比如创建、删除、修改、查看等。
# 权限设计模式
基于角色的访问控制(RBAC)
- 定义:RBAC 是最常见的权限控制模型,它基于“角色”来分配权限。用户通过角色获得访问某些资源的权限。每个角色可以拥有一组权限,而每个用户通过赋予不同的角色,来获得相应的权限。
- 特点:
- 简单直观,易于理解。
- 权限的管理集中在角色上,避免了直接为每个用户单独设置权限。
- 支持多角色和角色继承。
- 优点:
- 易于管理,减少权限配置的复杂度。
- 支持灵活的权限分配,如通过角色来控制不同部门、团队等的访问权限。
- 缺点:
- 对于权限非常细粒度的控制,可能会受到限制。
- 角色可能会变得复杂,尤其是需要多个角色组合时。
基于属性的访问控制(ABAC)
- 定义:ABAC 是基于用户、资源和环境的属性来控制访问权限。通过评估属性来决定用户是否可以访问某个资源。
- 特点:
- 更加灵活,支持复杂的权限控制。
- 权限控制不仅依赖于角色,还可以包括其他属性,如部门、职位、时间等。
- 优点:
- 适用于需要细粒度控制的复杂场景。
- 可以对多种条件进行评估,例如,用户的部门属性、文件的敏感等级等。
- 缺点:
- 相比 RBAC,ABAC 的设计和实现更为复杂。
- 在多维度的属性设计中,可能会遇到权限管理困难。
基于权限的访问控制(PBAC)
- 定义:PBAC 通过直接分配特定的权限给用户,来控制用户对资源的访问。每个权限代表一个资源或操作,用户通过权限来访问对应的资源。
- 特点:
- 更精细的权限控制,通过明确指定权限控制访问。
- 权限可以直接分配给用户,而不需要通过角色。
- 优点:
- 权限控制非常细粒度,能够精确控制每个用户能做什么。
- 适用于高安全性要求的场景。
- 缺点:
- 权限管理复杂,每个用户需要管理的权限可能非常多。
- 可能导致权限配置的维护成本较高。
# 权限设计的流程
需求分析
在设计权限之前,首先需要了解系统的需求。根据业务功能和安全需求,明确哪些资源需要进行权限控制,哪些用户可以访问哪些资源和操作。
- 资源划分:定义系统中的资源和操作,如数据表、页面、API 接口等。
- 用户角色分析:明确系统中不同用户的角色,如管理员、普通用户、财务人员等。
- 权限需求分析:根据业务需求分析用户需要访问的资源、功能及操作。
定义权限模型
根据需求,选择合适的权限模型,如 RBAC、ABAC 或 PBAC。通常,基于角色的访问控制(RBAC)是最常见的模型,适用于大多数企业应用。
- 角色定义:确定系统中需要的角色,例如管理员、普通用户、经理、访客等。
- 权限定义:确定每个角色应具有的权限,通常是基于资源的操作权限(如查看、编辑、删除等)。
- 权限和角色映射:为每个角色分配权限,确保每个角色可以访问他们需要的资源和执行相应的操作。
实现权限控制
在前端和后端实现权限控制。
- 前端控制:根据用户的角色和权限信息,动态控制用户界面的显示。例如,隐藏不允许访问的菜单项、按钮,禁止访问无权限的页面。
- 后端控制:后端需要根据用户的身份信息验证其是否有权限执行操作。例如,使用 JWT(JSON Web Token)来验证用户身份,并根据用户的角色来控制数据访问。
常见的做法是将用户角色和权限信息嵌入在 Token 中,后端在处理每个请求时,会验证 Token,并根据角色权限判断是否允许访问某个接口。
权限验证与授权
在权限验证时,系统会判断用户是否具备访问某个资源或执行某个操作的权限。常见的验证方法有:
- 访问控制列表(ACL):每个资源(如文件、数据表等)都有一个访问控制列表,列出了哪些用户或角色可以访问该资源。
- 角色和权限判断:在后端,检查用户的角色,基于角色来判断是否允许访问特定资源。
- 基于属性的规则:例如,检查用户的部门、职位等属性来决定其是否能访问某个资源。
审计与日志
权限设计中需要考虑日志记录和权限审计功能,便于对用户操作进行追踪和分析。日志记录可以包括用户登录、访问的资源、执行的操作等信息。通过审计日志,管理员可以监控权限滥用、恶意操作或潜在的安全问题。
持续优化和管理
随着系统的演进,权限模型可能需要调整和优化。需要定期评估和更新角色和权限的设计,确保权限模型的安全性和有效性。此外,权限的管理和审批流程也应随时调整,以应对业务需求变化。
# 权限设计的最佳实践
- 最小权限原则(Principle of Least Privilege):用户和角色应仅拥有执行其工作所需的最小权限,避免不必要的权限扩展。
- 权限分离(Separation of Duties):将敏感操作拆分为多个步骤,并要求不同的角色执行每个步骤。例如,财务人员和审批人员分别拥有不同的权限,避免一个角色拥有过多权限。
- 动态权限控制:根据业务需求和用户行为动态调整权限。例如,用户的权限可以根据时间、地点、设备等进行限制。
- 清晰的角色层次结构:设计角色时,尽量做到层次分明,避免角色过于复杂或重叠。
- 定期审查与更新权限:权限模型需要定期审查,特别是在用户角色或业务需求发生变化时,及时调整角色和权限配置。
权限设计是系统安全管理的核心部分,好的权限设计可以有效地保护系统不被未授权的访问,保障数据的隐私与完整性。选择合适的权限控制模型(RBAC、ABAC、PBAC),并根据业务需求、用户角色、操作权限进行合理配置,是权限设计的关键。与此同时,权限的维护、审计和优化也应是权限管理中的重要任务。
# 基于 RBAC 的权限设计
BAC(Role-Based Access Control)是一种通过角色来控制访问权限的模型,基于用户的角色来定义可以执行的操作和访问的资源。用户通过被分配到不同的角色,从而获得相应的权限。
核心概念:
- 用户(User):指系统中的使用者。
- 角色(Role):系统中的角色定义,代表了一组权限。角色可以是管理员、普通用户、访客等。
- 权限(Permission):定义了用户可以执行的操作,通常是与资源绑定的行为(如查看、编辑、删除等)。
- 资源(Resource):系统中的可操作对象,通常是页面、数据、文件、功能模块等。
- 操作(Action):用户可以对资源执行的操作,如查看(view)、修改(edit)、删除(delete)等。
在前端,RBAC 权限控制的重点通常是动态路由控制和UI 元素权限控制。前端通过根据用户角色和权限,动态渲染不同的页面、菜单、按钮等元素,确保用户只能访问其角色允许的功能。
- 获取用户角色和权限信息:用户登录后,前端需要从后端获取用户的角色和权限信息(如通过 JWT 或 session 存储)。
- 动态路由和菜单渲染:根据角色信息,动态生成路由和菜单,只显示当前角色可以访问的页面和功能。
- UI 控件的权限控制:根据角色权限动态控制界面上的按钮、输入框等组件,确保用户不能操作他们没有权限的元素。
在后端,RBAC 的核心是通过角色和权限控制用户的操作。通常,后端在接收到请求时,会根据用户的身份(通过 JWT、Session 等方式验证)来获取其角色,并根据角色权限来判断是否允许访问某个接口或执行某项操作。
- 用户认证:用户登录后,后端会验证用户身份,通常通过用户名和密码,或使用第三方认证(如 OAuth、JWT 等)。
- 角色和权限验证:用户登录后,后端会根据用户角色获取权限列表,然后根据请求的资源和操作进行权限验证。
- 授权控制:根据用户角色和权限,决定是否允许访问特定的资源。
# 问题
# Q1. Session-Cookie 与 Token 认证的区别?
Session-Cookie 认证 和 Token 认证 是两种常见的身份验证方式,每种方式都有其优缺点,适用于不同的场景。它们的工作原理和实现方式有所不同,具体可以从 认证原理、存储机制、扩展性、安全性 等方面进行比较。
# Session-Cookie 认证
工作原理:
- Session-Cookie 认证依赖于服务器端维护的会话(Session)。用户登录后,服务器创建一个 Session,并生成一个唯一的 Session ID,这个 Session ID 存储在浏览器的 Cookie 中,随每次请求发送到服务器。
- 服务器通过该 Session ID 查找存储在服务器端的会话数据,从而验证用户的身份。
流程:
- 用户通过用户名和密码登录。
- 服务器验证用户身份,并在服务器上创建一个 Session,并为该 Session 分配一个唯一的 Session ID。
- 服务器将 Session ID 存储在客户端的 Cookie 中。
- 用户每次发送请求时,浏览器会自动在请求头中附带该 Session ID,服务器通过该 Session ID 来验证用户身份。
- 当 Session 过期或用户登出时,服务器会销毁该 Session,客户端的 Cookie 也会被清除。
优缺点:
- 优点:
- 安全性较高:因为身份验证信息(Session ID)存储在服务器端,攻击者无法直接获取有效的身份信息。即使攻击者窃取了 Cookie,Cookie 本身也不会泄露身份数据,只有与服务器的 Session 匹配才有效。
- 服务器端控制:服务器可以主动控制 Session 的有效期、权限等,便于管理和清理无效会话。
- 缺点:
- 扩展性差:Session 存储在服务器端,每个用户的 Session 都需要占用服务器内存,随着用户数增加,服务器的负担加重,难以横向扩展(特别是在分布式架构中)。
- 负载均衡问题:当应用部署在多个服务器时,需要保证请求能始终路由到存储 Session 的同一台服务器,或者使用集中式的 Session 存储(如 Redis)。
- 跨域问题:Cookie 存储会受到浏览器的 Same-Origin Policy 限制,跨域请求时需要额外的配置支持。
# Token 认证(例如:JWT)
工作原理:
- Token 认证不依赖于服务器端的 Session,而是基于一个 Token(例如:JWT),通常是 JSON Web Token(JWT),来进行用户身份验证。
- 用户登录时,服务器验证用户身份后生成一个 Token,并将其返回给客户端。客户端保存 Token,通常保存在 LocalStorage 或 SessionStorage 中,后续的每个请求都携带该 Token(一般放在请求的 Authorization Header 中)发送给服务器。
- 服务器通过解析和验证 Token 来确定用户身份。由于 Token 是自包含的,它不需要依赖服务器端的存储,所有验证信息都包含在 Token 内。
流程:
- 用户通过用户名和密码登录。
- 服务器验证用户身份,生成一个 JWT(Token),并将其返回给客户端。
- 客户端将 Token 存储在 LocalStorage 或 SessionStorage 中(或者通过 Authorization Header 进行传递)。
- 客户端每次向服务器发送请求时,都会在请求头中携带该 Token。
- 服务器验证 Token 的合法性(通过签名、过期时间等机制),确认用户身份。
优缺点:
- 优点:
- 无状态(Stateless):Token 本身包含了用户身份信息,服务器不需要存储任何会话数据,因此可以方便地横向扩展,适合分布式架构。
- 跨域支持:由于 Token 存储在客户端,而不是依赖于浏览器的 Cookie,可以更方便地支持跨域请求。
- 灵活性强:Token 可以携带额外的自定义信息,如权限、角色等,且这种信息不容易被篡改,因为 JWT 通常会进行签名。
- 适合移动端应用:Token 认证的机制对于移动端应用特别友好,可以存储在客户端的本地存储中,无需担心 Cookie 的限制。
- 缺点:
- Token 被盗用的风险:如果 Token 存储在不安全的地方(如 LocalStorage),它可能会被恶意脚本窃取。为了提高安全性,Token 需尽量存储在 HttpOnly Cookie 中,但这又使得 Token 认证失去了一部分灵活性。
- Token 无法撤销:一旦 Token 被颁发并被客户端持有,服务器无法主动撤销或销毁 Token(除非实现 Token 黑名单机制),这意味着用户即使登出,Token 依然有效,直到过期。
- Token 可能较大:尤其是 JWT,由于它包含用户身份信息、权限、角色等数据,相比 Session ID,Token 通常更大,可能增加传输开销。
- 需要处理过期和刷新机制:Token 通常有过期时间,因此需要额外的刷新机制来保证用户的持续登录状态。
# Session-Cookie 认证 vs Token 认证的对比
特性 | Session-Cookie 认证 | Token 认证 (例如:JWT) |
---|---|---|
存储位置 | 服务器端存储 Session,客户端存储 Session ID | 客户端存储 Token(如 LocalStorage 或 Cookie) |
扩展性 | 随着用户数增加,服务器内存负担增加,不易扩展 | 无状态设计,适合分布式和大规模应用 |
跨域支持 | 不支持跨域(需要配置跨域 Cookie) | 由于不依赖 Cookie,容易支持跨域请求 |
安全性 | Session ID 存储在 Cookie 中,可能被窃取 | Token 可能被窃取,需保证存储安全 |
存储容量 | 只存储 Session ID,不会增加传输负担 | Token 较大,可能会影响传输效率 |
会话管理 | 服务器可以主动控制 Session 的生命周期 | 服务器无法主动撤销 Token,需要实现黑名单 |
适用场景 | 传统 Web 应用,依赖于服务器存储会话数据 | 适用于跨平台应用、API 认证、分布式系统 |
刷新机制 | 依赖于服务器存储的会话自动管理 | 需要通过 Refresh Token 来更新 Token |
Session-Cookie 认证适合传统的、单一的 Web 应用,尤其在用户量相对较小的情况下。它的安全性较好,但扩展性差,且跨域和负载均衡配置较为复杂。
Token 认证(例如 JWT) 适合分布式应用、微服务架构、API 认证、以及跨平台应用(Web、移动端等)。它的无状态性使得它更加灵活,易于扩展,但需要额外考虑 Token 的安全存储和生命周期管理。
# Q2. 什么是 JWT?
JWT(JSON Web Token)是一种轻量级的、基于 JSON 格式的安全令牌,用于在不同系统之间安全地传递信息。它广泛应用于身份验证和信息交换场景,特别是在基于 Token 的认证系统中(例如:OAuth 2.0、OpenID Connect 等)。该 Token 被设计为紧凑且安全的,特别适用于分布式站点的单点登录(SSO)场景。JWT 的声明一般被用来在身份提供者和服务提供者间传递被认证的用户身份信息,以便于从资源服务器获取资源,也可以增加一些额外的其它业务逻辑所必须的声明信息,该 Token 也可直接被用于认证,也可被加密。
什么是 Bearer?
Bearer Token (RFC 6750 (opens new window)) 用于授权访问资源,任何 Bearer 持有者都可以无差别地用它来访问相关的资源,而无需证明持有加密 key。一个 Bearer 代表授权范围、有效期,以及其他授权事项;一个 Bearer 在存储和传输过程中应当防止泄露,需实现 Transport Layer Security (TLS);一个 Bearer 有效期不能过长,过期后可用 Refresh Token 申请更新
# JWT 结构
JWT 是由三部分组成的字符串,三部分通过点(.
)分隔:
Header(头部):
通常包含两部分信息:类型(JWT)和 签名算法(如
HS256
或RS256
)。示例:
{ "alg": "HS256", "typ": "JWT" }
1
2
3
4
Payload(有效载荷):
载荷部分包含了声明(Claims),这些声明是一些关于实体(通常是用户)以及额外数据的语句。JWT 定义了三种类型的声明:
- Registered Claims:预定义的标准字段,如
iss
(发行者)、exp
(过期时间)、sub
(主题)等。 - Public Claims:由应用自定义的声明,用于交换信息。
- Private Claims:用户定义的、应用特定的声明,用于在服务提供者之间共享信息。
- Registered Claims:预定义的标准字段,如
示例:
{ "sub": "1234567890", "name": "John Doe", "iat": 1516239022 }
1
2
3
4
5
Signature(签名):
签名部分是用来验证 JWT 数据是否被篡改的,它是用 Header 和 Payload 以及一个密钥(
secret
)通过指定的算法(例如HS256
)生成的。签名的作用是防止 JWT 在传输过程中被篡改。签名的生成方法是:
HMACSHA256( base64UrlEncode(header) + "." + base64UrlEncode(payload), secret)
1
2
3
4
JWT 的组成部分示例:
header.payload.signature
# JWT 的工作原理
JWT 的主要用途是作为认证机制,特别是在 用户登录认证 或 API 认证 中。其工作原理通常包括以下几个步骤:
- 用户登录:用户提交用户名和密码,服务器验证用户身份。
- 生成 JWT:身份验证成功后,服务器生成一个包含用户信息和权限的 JWT,并将其返回给客户端。
- 客户端存储 JWT:客户端通常将 JWT 存储在 LocalStorage、SessionStorage 或 Cookie 中。
- 每次请求携带 JWT:在随后的请求中,客户端会将 JWT 附加到 HTTP 请求头中的
Authorization
字段,通常使用Bearer
方案Authorization: Bearer <token>
- 服务器验证 JWT:服务器接收到请求后,会验证 JWT 的有效性,确认其签名是否匹配,是否已过期等。
- 访问授权:如果 JWT 验证通过,服务器则会允许访问受保护的资源;如果验证失败,则返回 401 未授权错误。
# JWT 的优缺点
优点:
- 无状态(Stateless):JWT 本身包含了用户的身份信息和授权数据,不需要服务器维护会话状态。服务器不需要存储会话数据,因此适用于分布式架构和微服务系统。
- 易于扩展:由于 JWT 是自包含的,可以在 Token 中包含更多的自定义数据(如用户权限、角色等),方便扩展。
- 跨域支持:JWT 可以通过 HTTP 请求头进行传输,支持跨域认证,适合单页应用(SPA)和移动端应用。
- 支持多平台:JWT 不依赖于 Cookie,可以用于 Web 应用、移动应用和其他客户端。
缺点:
- Token 被盗用的风险:如果 JWT 存储不当(如存储在 LocalStorage 中),可能会被恶意脚本盗取。为此,JWT 需要注意存储方式,如使用 HttpOnly Cookie 来存储。
- Token 大小问题:由于 JWT 将信息嵌入在 Token 中,可能会比普通的 Session ID 更大,影响传输性能。
- 无法撤销:一旦 JWT 被生成并发放,除非过期,否则无法主动撤销(比如用户登出时)。需要通过 黑名单 或 Token 过期机制 来处理。
- 暴露敏感数据风险:JWT 的有效载荷部分是可以解码的,虽然签名确保数据未被篡改,但敏感信息可能暴露在 Token 中。可以通过加密 JWT 来提高安全性。
# Q3. id_token, access_token 和 refresh_token?
在现代身份验证和授权协议中,特别是基于 OAuth 2.0 和 OpenID Connect 的系统中,常用的三种令牌是 id_token
、access_token
和 refresh_token
。这三种令牌有各自的作用和生命周期,用于不同的身份验证和授权场景。
# ID Token(身份令牌)
定义:
id_token
是在 OpenID Connect 协议中引入的令牌,用于向客户端提供有关用户的身份信息。它通常是一个 JWT(JSON Web Token),包含了有关用户的声明(claims),例如用户的唯一标识、登录时间、过期时间等。id_token
是用于身份验证,告诉客户端谁正在进行操作,确认用户的身份。
用途:
id_token
主要用于 身份验证。它向客户端提供了关于用户的基本信息,尤其是在单点登录(SSO)场景中,确保客户端可以确认用户身份。- 在 OpenID Connect 中,
id_token
会在用户登录成功后返回,通常由授权服务器签发。 id_token
用于确认用户已通过身份验证,但它不用于访问保护资源。
生成过程:
- 用户通过身份验证并授权应用访问其资源。
- 授权服务器生成
id_token
,并将其返回给客户端。 id_token
通常包含用户的信息,如sub
(用户ID)、iat
(签发时间)、exp
(过期时间)等。
典型结构:
id_token
是一个 JWT,通常包含以下几个部分:- Header(头部):通常包含签名算法和类型(
JWT
)。 - Payload(有效载荷):包含用户信息和其他声明(
claims
)。 - Signature(签名):用密钥签名
header
和payload
,确保数据没有被篡改。
- Header(头部):通常包含签名算法和类型(
示例:
{
"iss": "https://example.com",
"sub": "1234567890",
"aud": "your-client-id",
"exp": 1311281970,
"iat": 1311280970,
"name": "John Doe"
}
2
3
4
5
6
7
8
生命周期:
id_token
的有效期一般比较短,通常在 5 分钟到 1 小时之间,取决于授权服务器的设置。
# Access Token(访问令牌)
定义:
access_token
是用于授权客户端访问受保护资源的令牌。它代表了用户授权给客户端应用访问某些资源的权限。access_token
的有效期较短,通常是几分钟到几小时。
用途:
access_token
主要用于 授权访问 API 或其他受保护资源。客户端在向服务器请求资源时,通常需要携带access_token
以证明其已获得访问权限。access_token
会在 OAuth 2.0 授权流程中返回,客户端会将其用于后续的 API 请求。
生成过程:
- 客户端应用请求授权,用户进行身份验证并授权。
- 授权服务器生成
access_token
并返回给客户端。 - 客户端在后续请求中将
access_token
附加在请求头中,通常是Authorization: Bearer <access_token>
。
特点:
- 短期有效:
access_token
的有效期通常较短,目的在于减少长期有效令牌带来的风险。 - 包含授权信息:
access_token
包含了用户授权的信息,用于允许客户端访问受保护资源,但它不包含身份信息。
风险:
- 如果
access_token
被泄露,恶意用户可以用它访问用户的数据。因此,需要通过加密通道(如 HTTPS)传输,并采用适当的存储方式(如 HttpOnly Cookies)来增强安全性。
# Refresh Token(刷新令牌)
定义:
refresh_token
是用于刷新access_token
的令牌,通常与access_token
一起颁发。它是一个长期有效的令牌,客户端可以使用它来获取新的access_token
,而无需再次请求用户的登录凭证。refresh_token
主要用于处理access_token
过期时的情况。
用途:
refresh_token
的作用是 刷新过期的access_token
。当access_token
过期时,客户端可以使用refresh_token
向授权服务器请求新的access_token
,从而保持用户的登录状态。- 它通常用于客户端与授权服务器之间的长期认证,避免频繁要求用户重新登录。
生成过程:
- 用户成功登录并授权客户端应用访问其资源后,授权服务器生成
access_token
和refresh_token
。 - 客户端存储
access_token
和refresh_token
,并在access_token
过期时使用refresh_token
获取新的access_token
。
特点:
- 长期有效:
refresh_token
通常具有较长的有效期,可以是几天、几周甚至更长时间。 - 仅用于刷新
access_token
:它不用于访问资源,而是通过提供新的access_token
来延续用户的会话。 - 只能在授权服务器上使用:
refresh_token
只应传递给授权服务器,服务器根据refresh_token
生成新的access_token
。
安全性:
refresh_token
的安全性较为关键,如果它被窃取,攻击者可能会持续获得新的access_token
,因此需要安全存储,并且通常需要附加更多的安全措施(如 IP 地址检查或设备绑定等)。
特性 | ID Token | Access Token | Refresh Token |
---|---|---|---|
主要作用 | 身份验证(确认用户身份) | 授权访问受保护资源(API) | 刷新 access_token ,延续会话 |
典型使用场景 | 登录验证、SSO | API 请求、资源访问 | 用户会话保持、刷新访问权限 |
包含的信息 | 用户身份信息(如用户名、邮箱等) | 授权信息(如访问权限、作用域) | 无,主要用于刷新 access_token |
有效期 | 短期(通常 5 分钟到 1 小时) | 短期(通常几分钟到几小时) | 长期(通常几天到几周) |
安全性 | 通常通过 HTTPS 传输,避免泄露 | 需要保护,防止泄露 | 需要保护,防止泄露 |
是否可以刷新 | 不可以刷新 | 不可以刷新 | 用于刷新 access_token |
# 完整的授权流程示例(OAuth 2.0 + OpenID Connect)
用户登录:
- 用户访问客户端应用,点击登录按钮,客户端将用户重定向到授权服务器(例如,Google 或 Facebook)。
授权服务器验证身份:
- 用户在授权服务器上输入用户名和密码,完成身份验证后,授权服务器生成并返回一个
id_token
和一个access_token
(以及可能的refresh_token
)。
- 用户在授权服务器上输入用户名和密码,完成身份验证后,授权服务器生成并返回一个
客户端存储令牌:
- 客户端保存
id_token
、access_token
和refresh_token
(通常将id_token
用于身份验证,access_token
用于访问资源,refresh_token
用于刷新access_token
)。
- 客户端保存
请求受保护资源:
- 客户端通过
access_token
访问资源服务器上的数据,通常将access_token
放在 HTTP 请求的 Authorization 头中。
- 客户端通过
Token 刷新:
- 当
access_token
过期时,客户端使用refresh_token
请求授权服务器生成新的access_token
,从而继续访问资源。
- 当
id_token
主要用于 身份验证,包含了用户身份信息。access_token
主要用于 授权访问 受保护的资源(例如 API),它的有效期较短。refresh_token
用于 刷新access_token
,避免频繁的用户登录,通常有效期较长。
← 大文件上传 WebTransport →