数据定制与验证
注意: 在你添加自己的自定义数据之前,请注意 JWT payload 未加密,仅经过 base64 编码。 令牌签名确保其完整性(意味着它不能被修改),但任何人都可以读取其内容(使用像 https://jwt.node.org.cn/ 这样的简单工具尝试一下)。
向 JWT 添加自定义数据或标头
使用 Events::JWT_CREATED
默认情况下,JWT payload 将包含用户名、用户角色、令牌创建日期和过期日期,但你可以添加自己的数据。
你也可以修改标头以适应你的应用程序上下文。
1 2 3 4 5 6 7
# config/services.yaml
services:
acme_api.event.jwt_created_listener:
class: App\EventListener\JWTCreatedListener
arguments: [ '@request_stack' ]
tags:
- { name: kernel.event_listener, event: lexik_jwt_authentication.on_jwt_created, method: onJWTCreated }
示例:向编码后的 payload 添加客户端 IP 地址
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 30 31 32 33 34 35 36 37
// src/App/EventListener/JWTCreatedListener.php
use Lexik\Bundle\JWTAuthenticationBundle\Event\JWTCreatedEvent;
use Symfony\Component\HttpFoundation\RequestStack;
/**
* @var RequestStack
*/
private $requestStack;
/**
* @param RequestStack $requestStack
*/
public function __construct(RequestStack $requestStack)
{
$this->requestStack = $requestStack;
}
/**
* @param JWTCreatedEvent $event
*
* @return void
*/
public function onJWTCreated(JWTCreatedEvent $event)
{
$request = $this->requestStack->getCurrentRequest();
$payload = $event->getData();
$payload['ip'] = $request->getClientIp();
$event->setData($payload);
$header = $event->getHeader();
$header['cty'] = 'JWT';
$event->setHeader($header);
}
示例:覆盖令牌过期日期计算以使其更灵活
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
// src/App/EventListener/JWTCreatedListener.php
use Lexik\Bundle\JWTAuthenticationBundle\Event\JWTCreatedEvent;
/**
* @param JWTCreatedEvent $event
*
* @return void
*/
public function onJWTCreated(JWTCreatedEvent $event)
{
$expiration = new \DateTime('+1 day');
$expiration->setTime(2, 0, 0);
$payload = $event->getData();
$payload['exp'] = $expiration->getTimestamp();
$event->setData($payload);
}
在 JWT 创建时使用自定义 payload
如果你以编程方式创建 JWT 令牌,你可以使用 createFromPayload(UserInterface $user, array $payload)
方法向 JWT 添加自定义数据
1 2 3
$payload = ['foo' => 'bar'];
$jwt = $this->container->get('lexik_jwt_authentication.jwt_manager')->createFromPayload($user, $payload);
Events::JWT_DECODED - 验证 JWT payload 中的数据
你可以访问已解码的 jwt payload,以执行你自己的额外验证。
1 2 3 4 5 6 7
# config/services.yaml
services:
acme_api.event.jwt_decoded_listener:
class: App\EventListener\JWTDecodedListener
arguments: [ '@request_stack' ]
tags:
- { name: kernel.event_listener, event: lexik_jwt_authentication.on_jwt_decoded, method: onJWTDecoded }
示例:检查已解码 payload 中的客户端 IP (来自示例 1)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
// src/App/EventListener/JWTDecodedListener.php
use Lexik\Bundle\JWTAuthenticationBundle\Event\JWTDecodedEvent;
/**
* @param JWTDecodedEvent $event
*
* @return void
*/
public function onJWTDecoded(JWTDecodedEvent $event)
{
$request = $this->requestStack->getCurrentRequest();
$payload = $event->getPayload();
if (!isset($payload['ip']) || $payload['ip'] !== $request->getClientIp()) {
$event->markAsInvalid();
}
}
示例:向 payload 添加额外数据 - 以便在你的 自定义 UserProvider 中获取
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
// src/App/EventListener/JWTDecodedListener.php
use Lexik\Bundle\JWTAuthenticationBundle\Event\JWTDecodedEvent;
/**
* @param JWTDecodedEvent $event
*
* @return void
*/
public function onJWTDecoded(JWTDecodedEvent $event)
{
$payload = $event->getPayload();
$user = $this->userRepository->findOneByUsername($payload['username']);
$payload['custom_user_data'] = $user->getCustomUserInformations();
$event->setPayload($payload); // Don't forget to regive the payload for next event / step
}
Events::JWT_AUTHENTICATED - 自定义你的安全令牌
你可以在令牌通过身份验证后向令牌添加属性,以允许你的应用程序使用 JWT 属性。
1 2 3 4 5 6
# config/services.yaml
services:
acme_api.event.jwt_authenticated_listener:
class: App\EventListener\JWTAuthenticatedListener
tags:
- { name: kernel.event_listener, event: lexik_jwt_authentication.on_jwt_authenticated, method: onJWTAuthenticated }
示例:在已验证的令牌中保留 JWT 中设置的 UUID
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
// src/App/EventListener/JWTAuthenticatedListener.php
use Lexik\Bundle\JWTAuthenticationBundle\Event\JWTAuthenticatedEvent;
/**
* @param JWTAuthenticatedEvent $event
*
* @return void
*/
public function onJWTAuthenticated(JWTAuthenticatedEvent $event)
{
$token = $event->getToken();
$payload = $event->getPayload();
$token->setAttribute('uuid', $payload['uuid']);
}
Events::AUTHENTICATION_SUCCESS - 向 JWT 响应添加公共数据
默认情况下,身份验证响应只是一个包含 JWT 的 json,但你可以向其中添加你自己的公共数据。
1 2 3 4 5 6
# config/services.yaml
services:
acme_api.event.authentication_success_listener:
class: App\EventListener\AuthenticationSuccessListener
tags:
- { name: kernel.event_listener, event: lexik_jwt_authentication.on_authentication_success, method: onAuthenticationSuccessResponse }
示例:向响应体添加用户角色
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
// src/App/EventListener/AuthenticationSuccessListener.php
use Lexik\Bundle\JWTAuthenticationBundle\Event\AuthenticationSuccessEvent;
/**
* @param AuthenticationSuccessEvent $event
*/
public function onAuthenticationSuccessResponse(AuthenticationSuccessEvent $event)
{
$data = $event->getData();
$user = $event->getUser();
if (!$user instanceof UserInterface) {
return;
}
$data['data'] = array(
'roles' => $user->getRoles(),
);
$event->setData($data);
}
Events::JWT_ENCODED - 获取编码后的 JWT 令牌字符串
你可能需要在 JWT 创建后获取它。
示例:获取 JWT 字符串
1 2 3 4 5 6 7 8 9 10
// src/App/EventListener/JWTEncodedListener.php
use Lexik\Bundle\JWTAuthenticationBundle\Event\JWTEncodedEvent;
/**
* @param JWTEncodedEvent $event
*/
public function onJwtEncoded(JWTEncodedEvent $event)
{
$token = $event->getJWTString();
}
Events::AUTHENTICATION_FAILURE - 自定义失败响应体
默认情况下,身份验证失败时的响应只是一个包含失败消息和 401 状态码的 json,但你可以设置自定义响应。
1 2 3 4 5 6
# config/services.yaml
services:
acme_api.event.authentication_failure_listener:
class: App\EventListener\AuthenticationFailureListener
tags:
- { name: kernel.event_listener, event: lexik_jwt_authentication.on_authentication_failure, method: onAuthenticationFailureResponse }
示例:设置身份验证失败时的自定义响应
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
// src/App/EventListener/AuthenticationFailureListener.php
use Lexik\Bundle\JWTAuthenticationBundle\Event\AuthenticationFailureEvent;
use Lexik\Bundle\JWTAuthenticationBundle\Response\JWTAuthenticationFailureResponse;
use Symfony\Component\HttpFoundation\JsonResponse;
/**
* @param AuthenticationFailureEvent $event
*/
public function onAuthenticationFailureResponse(AuthenticationFailureEvent $event)
{
$data = [
'name' => 'John Doe',
'foo' => 'bar',
];
$response = new JWTAuthenticationFailureResponse('Bad credentials, please verify that your username/password are correctly set', JsonResponse::HTTP_UNAUTHORIZED);
$response->setData($data);
$event->setResponse($response);
}
Events::JWT_INVALID - 自定义无效令牌响应
默认情况下,如果令牌无效,则响应只是一个包含相应错误消息和 401 状态码的 json,但你可以设置自定义响应。
1 2 3 4 5 6
# config/services.yaml
services:
acme_api.event.jwt_invalid_listener:
class: App\EventListener\JWTInvalidListener
tags:
- { name: kernel.event_listener, event: lexik_jwt_authentication.on_jwt_invalid, method: onJWTInvalid }
示例:在无效令牌上设置自定义响应消息和状态码
1 2 3 4 5 6 7 8 9 10 11 12 13
// src/App/EventListener/JWTInvalidListener.php
use Lexik\Bundle\JWTAuthenticationBundle\Event\JWTInvalidEvent;
use Lexik\Bundle\JWTAuthenticationBundle\Response\JWTAuthenticationFailureResponse;
/**
* @param JWTInvalidEvent $event
*/
public function onJWTInvalid(JWTInvalidEvent $event)
{
$response = new JWTAuthenticationFailureResponse('Your token is invalid, please login again to get a new one', 403);
$event->setResponse($response);
}
Events::JWT_NOT_FOUND - 自定义未找到令牌的响应
默认情况下,如果在请求中未找到令牌,身份验证监听器将调用返回未经授权 (401) json 响应的入口点,或者(如果防火墙允许匿名请求),则让请求继续。
感谢此事件,你可以设置自定义响应。
1 2 3 4 5 6
# config/services.yaml
services:
acme_api.event.jwt_notfound_listener:
class: App\EventListener\JWTNotFoundListener
tags:
- { name: kernel.event_listener, event: lexik_jwt_authentication.on_jwt_not_found, method: onJWTNotFound }
示例:设置未找到令牌的自定义响应消息
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
// src/App/EventListener/JWTNotFoundListener.php
use Lexik\Bundle\JWTAuthenticationBundle\Event\JWTNotFoundEvent;
use Symfony\Component\HttpFoundation\JsonResponse;
/**
* @param JWTNotFoundEvent $event
*/
public function onJWTNotFound(JWTNotFoundEvent $event)
{
$data = [
'status' => '403 Forbidden',
'message' => 'Missing token',
];
$response = new JsonResponse($data, 403);
$event->setResponse($response);
}
Events::JWT_EXPIRED - 自定义过期令牌的响应消息
默认情况下,如果请求中提供的令牌已过期,身份验证监听器将调用入口点,返回未经授权 (401) json 响应。 感谢此事件,你可以设置自定义响应或仅更改响应消息。
1 2 3 4 5 6
# config/services.yaml
services:
acme_api.event.jwt_expired_listener:
class: App\EventListener\JWTExpiredListener
tags:
- { name: kernel.event_listener, event: lexik_jwt_authentication.on_jwt_expired, method: onJWTExpired }
示例:自定义过期令牌情况下的响应
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
// src/App/EventListener/JWTExpiredListener.php
use Lexik\Bundle\JWTAuthenticationBundle\Event\JWTExpiredEvent;
use Lexik\Bundle\JWTAuthenticationBundle\Response\JWTAuthenticationFailureResponse;
/**
* @param JWTExpiredEvent $event
*/
public function onJWTExpired(JWTExpiredEvent $event)
{
/** @var JWTAuthenticationFailureResponse */
$response = $event->getResponse();
$response->setMessage('Your token is expired, please renew it.');
}
小贴士: 你可能希望使用相同的方法来自定义 JWT_INVALID
、JWT_NOT_FOUND
和/或 JWT_EXPIRED
事件的响应。 为此,请使用 Lexik
接口来类型提示你的监听器方法的事件参数,而不是对应于这些特定事件之一的具体类。