Symfony 6中LexikJWTAuthenticationBundle按用户ID/用户名删Token并强制登出
在Symfony 6中处理LexikJWT Token失效与登录重定向
一、通过用户ID/用户名使LexikJWT Token失效
LexikJWTAuthenticationBundle本身是无状态设计,Token签发后服务端默认不存储,因此无法直接删除指定用户的Token。要实现通过用户ID或用户名让该用户所有Token失效,需搭建Token黑名单机制,具体步骤如下:
1. 选择存储方案
可选用Redis或数据库存储失效用户/Token信息:
- Redis:用
jwt_blacklist:{user_id}作为键,存储Token过期时间或Token列表,适合高性能场景。 - 数据库:创建
JwtBlacklist实体,包含userId、expiresAt(失效截止时间)等字段。
2. 拦截Token验证流程
自定义Token验证器,在每次请求时检查用户是否被拉黑:
// src/Security/Validator/BlacklistedUserValidator.php namespace App\Security\Validator; use Lexik\Bundle\JWTAuthenticationBundle\Encoder\JWTEncoderInterface; use Lexik\Bundle\JWTAuthenticationBundle\TokenExtractor\TokenExtractorInterface; use Symfony\Component\HttpFoundation\Request; use Doctrine\ORM\EntityManagerInterface; class BlacklistedUserValidator { public function __construct( private TokenExtractorInterface $tokenExtractor, private JWTEncoderInterface $jwtEncoder, private EntityManagerInterface $em ) {} public function isUserBlacklisted(Request $request): bool { $token = $this->tokenExtractor->extract($request); if (!$token) { return false; } $payload = $this->jwtEncoder->decode($token); $userId = $payload['id']; // 检查用户是否在黑名单中 $blacklistedEntry = $this->em->getRepository(JwtBlacklist::class)->findOneBy(['userId' => $userId]); return $blacklistedEntry !== null && $blacklistedEntry->getExpiresAt() > new \DateTime(); } }
3. 实现拉黑用户的方法
编写服务类,支持通过用户ID或用户名拉黑用户:
// src/Service/JwtBlacklistService.php namespace App\Service; use Doctrine\ORM\EntityManagerInterface; use App\Entity\User; use App\Entity\JwtBlacklist; class JwtBlacklistService { public function __construct(private EntityManagerInterface $em) {} public function blacklistUserById(int $userId): void { $blacklistEntry = new JwtBlacklist(); $blacklistEntry->setUserId($userId); // 设置为Token最大有效期,确保所有未过期Token失效 $blacklistEntry->setExpiresAt(new \DateTime('+1 day')); $this->em->persist($blacklistEntry); $this->em->flush(); } public function blacklistUserByUsername(string $username): void { $user = $this->em->getRepository(User::class)->findOneBy(['username' => $username]); if ($user) { $this->blacklistUserById($user->getId()); } } }
4. 集成到安全验证流程
监听lexik_jwt_authentication.on_jwt_decoded事件,在Token解码后检查用户是否被拉黑,若拉黑则抛出异常终止请求:
// src/EventListener/JwtDecodedListener.php namespace App\EventListener; use App\Security\Validator\BlacklistedUserValidator; use Lexik\Bundle\JWTAuthenticationBundle\Event\JWTDecodedEvent; use Symfony\Component\Security\Core\Exception\AuthenticationException; class JwtDecodedListener { public function __construct(private BlacklistedUserValidator $validator) {} public function onJWTDecoded(JWTDecodedEvent $event): void { $request = $event->getRequest(); if ($this->validator->isUserBlacklisted($request)) { throw new AuthenticationException('Your account has been logged out.'); } } }
二、登录状态下自动重定向至登出页面
结合黑名单机制,当用户持有有效Token但已被拉黑时,在请求处理阶段触发重定向:
1. 创建请求监听事件
监听kernel.request事件,检查用户状态并跳转:
// src/EventListener/BlacklistedUserRedirectListener.php namespace App\EventListener; use App\Security\Validator\BlacklistedUserValidator; use Symfony\Component\HttpFoundation\RedirectResponse; use Symfony\Component\HttpKernel\Event\RequestEvent; use Symfony\Component\Routing\Generator\UrlGeneratorInterface; class BlacklistedUserRedirectListener { public function __construct( private BlacklistedUserValidator $validator, private UrlGeneratorInterface $urlGenerator ) {} public function onKernelRequest(RequestEvent $event): void { if (!$event->isMainRequest()) { return; } $request = $event->getRequest(); // 排除登出路由,避免循环跳转 if ($request->attributes->get('_route') === 'app_logout') { return; } if ($this->validator->isUserBlacklisted($request)) { $response = new RedirectResponse($this->urlGenerator->generate('app_logout')); $event->setResponse($response); } } }
2. 配置登出路由与安全规则
在routes.yaml定义登出路由:
app_logout: path: /logout
在security.yaml配置登出处理:
security: firewalls: main: logout: path: app_logout target: /login # 登出后跳转至登录页
内容的提问来源于stack exchange,提问作者echalh adil
相关产品推荐
相关产品推荐

