如何使用访问令牌认证
访问令牌或 API 令牌通常用作 API 上下文中的身份验证机制。访问令牌是一个字符串,在身份验证期间(使用应用程序或授权服务器)获得。访问令牌的作用是在颁发令牌之前验证用户身份并获得同意。
访问令牌可以是任何类型,例如不透明字符串、JSON Web Tokens (JWT) 或 SAML2(XML 结构)。有关详细规范,请参阅 RFC6750:OAuth 2.0 授权框架:Bearer 令牌用法。
使用访问令牌认证器
本指南假定您已设置安全性并在您的应用程序中创建了用户对象。如果尚未完成,请遵循主安全指南。
1) 配置访问令牌认证器
要使用访问令牌认证器,您必须配置一个 token_handler
。令牌处理程序从请求中接收令牌并返回正确的用户标识符。为了获取用户标识符,实现可能需要加载和验证令牌(例如,撤销、过期时间、数字签名等)。
1 2 3 4 5 6
# config/packages/security.yaml
security:
firewalls:
main:
access_token:
token_handler: App\Security\AccessTokenHandler
此处理程序必须实现 AccessTokenHandlerInterface
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
// src/Security/AccessTokenHandler.php
namespace App\Security;
use App\Repository\AccessTokenRepository;
use Symfony\Component\Security\Core\Exception\BadCredentialsException;
use Symfony\Component\Security\Http\AccessToken\AccessTokenHandlerInterface;
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge;
class AccessTokenHandler implements AccessTokenHandlerInterface
{
public function __construct(
private AccessTokenRepository $repository
) {
}
public function getUserBadgeFrom(string $accessToken): UserBadge
{
// e.g. query the "access token" database to search for this token
$accessToken = $this->repository->findOneByValue($accessToken);
if (null === $accessToken || !$accessToken->isValid()) {
throw new BadCredentialsException('Invalid credentials.');
}
// and return a UserBadge object containing the user identifier from the found token
// (this is the same identifier used in Security configuration; it can be an email,
// a UUID, a username, a database ID, etc.)
return new UserBadge($accessToken->getUserId());
}
}
访问令牌认证器将使用返回的用户标识符,通过 用户提供器 加载用户。
警告
检查令牌是否有效非常重要。例如,上面的示例验证令牌是否已过期。对于自包含访问令牌(如 JWT),处理程序需要验证数字签名并理解所有声明,尤其是 sub
、iat
、nbf
和 exp
。
2) 配置令牌提取器 (可选)
应用程序现在可以处理传入的令牌。令牌提取器从请求中检索令牌(例如,标头或请求正文)。
默认情况下,访问令牌从请求头参数 Authorization
中读取,方案为 Bearer
(例如,Authorization: Bearer the-token-value
)。
Symfony 按照 RFC6750 提供了其他提取器
header
(默认)- 令牌通过请求头发送。通常是带有
Bearer
方案的Authorization
。 query_string
- 令牌是请求查询字符串的一部分。通常是
access_token
。 request_body
- 令牌是 POST 请求期间请求正文的一部分。通常是
access_token
。
警告
由于与 URI 方法相关的安全弱点,包括 URL 或包含访问令牌的请求正文很可能被记录,因此 不应 使用 query_string
和 request_body
方法,除非无法在请求头字段中传输访问令牌。
您还可以创建自定义提取器。该类必须实现 AccessTokenExtractorInterface。
1 2 3 4 5 6 7 8 9 10 11 12
# config/packages/security.yaml
security:
firewalls:
main:
access_token:
token_handler: App\Security\AccessTokenHandler
# use a different built-in extractor
token_extractors: request_body
# or provide the service ID of a custom extractor
token_extractors: 'App\Security\CustomTokenExtractor'
可以设置多个提取器。在这种情况下,顺序很重要:列表中的第一个先被调用。
1 2 3 4 5 6 7 8 9
# config/packages/security.yaml
security:
firewalls:
main:
access_token:
token_handler: App\Security\AccessTokenHandler
token_extractors:
- 'header'
- 'App\Security\CustomTokenExtractor'
3) 提交请求
就这样!你的应用程序现在可以使用 API 令牌验证传入的请求。
使用默认的头提取器,你可以通过提交如下请求来测试该功能
1 2
$ curl -H 'Authorization: Bearer an-accepted-token-value' \
https://127.0.0.1:8000/api/some-route
自定义成功处理器
默认情况下,请求继续(例如,路由的控制器运行)。如果你想自定义成功处理,通过创建一个实现 AuthenticationSuccessHandlerInterface 的类,并将服务 ID 配置为 success_handler
来创建你自己的成功处理程序
1 2 3 4 5 6 7
# config/packages/security.yaml
security:
firewalls:
main:
access_token:
token_handler: App\Security\AccessTokenHandler
success_handler: App\Security\Authentication\AuthenticationSuccessHandler
提示
如果你想自定义默认的失败处理,请使用 failure_handler
选项,并创建一个实现 AuthenticationFailureHandlerInterface 的类。
使用 OpenID Connect (OIDC)
OpenID Connect (OIDC) 是第三代 OpenID 技术,它是一个 RESTful HTTP API,使用 JSON 作为其数据格式。OpenID Connect 是 OAuth 2.0 授权框架之上的身份验证层。它允许基于授权服务器执行的身份验证来验证最终用户的身份。
1) 配置 OidcUserInfoTokenHandler
OidcUserInfoTokenHandler
需要 symfony/http-client
包来发出所需的 HTTP 请求。如果你尚未安装它,请运行此命令
1
$ composer require symfony/http-client
Symfony 提供了一个通用的 OidcUserInfoTokenHandler
来调用你的 OIDC 服务器并检索用户信息
1 2 3 4 5 6 7
# config/packages/security.yaml
security:
firewalls:
main:
access_token:
token_handler:
oidc_user_info: https://www.example.com/realms/demo/protocol/openid-connect/userinfo
按照 OpenID Connect 规范,默认情况下 sub
声明用作用户标识符。要使用另一个声明,请在配置中指定它
1 2 3 4 5 6 7 8 9
# config/packages/security.yaml
security:
firewalls:
main:
access_token:
token_handler:
oidc_user_info:
claim: email
base_uri: https://www.example.com/realms/demo/protocol/openid-connect/userinfo
oidc_user_info
令牌处理程序会自动创建一个带有指定 base_uri
的 HTTP 客户端。如果你更喜欢使用自己的客户端,可以通过 client
选项指定服务名称
1 2 3 4 5 6 7 8
# config/packages/security.yaml
security:
firewalls:
main:
access_token:
token_handler:
oidc_user_info:
client: oidc.client
默认情况下,OidcUserInfoTokenHandler
使用声明创建一个 OidcUser
。要从声明中创建你自己的用户对象,你必须创建你自己的 UserProvider
1 2 3 4 5 6 7 8 9 10
// src/Security/Core/User/OidcUserProvider.php
use Symfony\Component\Security\Core\User\AttributesBasedUserProviderInterface;
class OidcUserProvider implements AttributesBasedUserProviderInterface
{
public function loadUserByIdentifier(string $identifier, array $attributes = []): UserInterface
{
// implement your own logic to load and return the user object
}
}
2) 配置 OidcTokenHandler
OidcTokenHandler
需要 web-token/jwt-library
包。如果你尚未安装它,请运行此命令
1
$ composer require web-token/jwt-library
Symfony 提供了一个通用的 OidcTokenHandler
来解码你的令牌、验证它并从中检索用户信息
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
# config/packages/security.yaml
security:
firewalls:
main:
access_token:
token_handler:
oidc:
# Algorithms used to sign the JWS
algorithms: ['ES256', 'RS256']
# A JSON-encoded JWK
keyset: '{"keys":[{"kty":"...","k":"..."}]}'
# Audience (`aud` claim): required for validation purpose
audience: 'api-example'
# Issuers (`iss` claim): required for validation purpose
issuers: ['https://oidc.example.com']
7.1
对用于签署 JWS 的多种算法的支持在 Symfony 7.1 中引入。在以前的版本中,仅支持 ES256
算法。
按照 OpenID Connect 规范,默认情况下 sub
声明用作用户标识符。要使用另一个声明,请在配置中指定它
1 2 3 4 5 6 7 8 9 10 11 12
# config/packages/security.yaml
security:
firewalls:
main:
access_token:
token_handler:
oidc:
claim: email
algorithms: ['ES256', 'RS256']
keyset: '{"keys":[{"kty":"...","k":"..."}]}'
audience: 'api-example'
issuers: ['https://oidc.example.com']
默认情况下,OidcTokenHandler
使用声明创建一个 OidcUser
。要从声明中创建你自己的用户,你必须创建你自己的 UserProvider
1 2 3 4 5 6 7 8 9 10
// src/Security/Core/User/OidcUserProvider.php
use Symfony\Component\Security\Core\User\AttributesBasedUserProviderInterface;
class OidcUserProvider implements AttributesBasedUserProviderInterface
{
public function loadUserByIdentifier(string $identifier, array $attributes = []): UserInterface
{
// implement your own logic to load and return the user object
}
}
使用 CAS 2.0
7.1
对 CAS 令牌处理程序的支持在 Symfony 7.1 中引入。
中央身份验证服务 (CAS) 是一种企业多语言单点登录解决方案和 Web 身份提供商,旨在成为满足您的身份验证和授权需求的综合平台。
配置 Cas2Handler
Symfony 提供了一个通用的 Cas2Handler
来调用你的 CAS 服务器。它需要 symfony/http-client
包来发出所需的 HTTP 请求。如果你尚未安装它,请运行此命令
1
$ composer require symfony/http-client
你可以按如下方式配置 cas
令牌处理程序
1 2 3 4 5 6 7 8
# config/packages/security.yaml
security:
firewalls:
main:
access_token:
token_handler:
cas:
validation_url: https://www.example.com/cas/validate
cas
令牌处理程序会自动创建一个 HTTP 客户端来调用指定的 validation_url
。如果你更喜欢使用自己的客户端,可以通过 http_client
选项指定服务名称
1 2 3 4 5 6 7 8 9
# config/packages/security.yaml
security:
firewalls:
main:
access_token:
token_handler:
cas:
validation_url: https://www.example.com/cas/validate
http_client: cas.client
- 默认情况下,令牌处理程序将使用
cas
前缀读取验证 URL XML 响应,但你可以配置另一个前缀 -
cas
前缀,但你可以配置另一个前缀
1 2 3 4 5 6 7 8 9
# config/packages/security.yaml
security:
firewalls:
main:
access_token:
token_handler:
cas:
validation_url: https://www.example.com/cas/validate
prefix: cas-example
从令牌创建用户
某些类型的令牌(例如 OIDC)包含创建用户实体所需的所有信息(例如,用户名和角色)。在这种情况下,你不需要用户提供器来从数据库创建用户
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
// src/Security/AccessTokenHandler.php
namespace App\Security;
// ...
class AccessTokenHandler implements AccessTokenHandlerInterface
{
// ...
public function getUserBadgeFrom(string $accessToken): UserBadge
{
// get the data from the token
$payload = ...;
return new UserBadge(
$payload->getUserId(),
fn (string $userIdentifier) => new User($userIdentifier, $payload->getRoles())
);
}
}
当使用此策略时,你可以为 无状态防火墙 省略 user_provider
配置。