我使用 symfony、lexik 来生成令牌,使用 gesdinet 来刷新令牌。 我在注销时遇到问题。
lexik_jwt_authentication:
secret_key: '%env(resolve:JWT_SECRET_KEY)%'
public_key: '%env(resolve:JWT_PUBLIC_KEY)%'
pass_phrase: '%env(JWT_PASSPHRASE)%'
token_ttl: 300 # 5min
gesdinet_jwt_refresh_token:
refresh_token_class: App\Entity\RefreshToken # Scaffolded by the bundle recipe
ttl: 7200 # 2h in seconds
single_use: true
# Use cookies for the refresh token
cookie:
enabled: true
remove_token_from_body: true
# Cookie parameters
http_only: true
same_site: strict # tylko dla HTTPS
secure: true
path: /api/token
domain: null
symfony.yaml
security:
enable_authenticator_manager: true
# https://symfony.com/doc/current/security.html#registering-the-user-hashing-passwords
password_hashers:
Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface: 'auto'
# https://symfony.com/doc/current/security.html#loading-the-user-the-user-provider
providers:
app_user_provider:
entity:
class: App\Entity\User
property: email
firewalls:
login:
pattern: ^/api/login
stateless: true
json_login:
check_path: /api/login_check
success_handler: lexik_jwt_authentication.handler.authentication_success
failure_handler: lexik_jwt_authentication.handler.authentication_failure
logout:
path: app_logout
csrf_token_generator: security.csrf.token_manager
refresh_token:
pattern: ^/api/token
stateless: true
refresh_jwt:
check_path: gesdinet_jwt_refresh_token
api:
pattern: ^/api
stateless: true
jwt: ~
access_control:
- { path: ^/api/login, roles: PUBLIC_ACCESS }
- { path: ^/api/(login|refresh), roles: PUBLIC_ACCESS }
- { path: ^/api, roles: IS_AUTHENTICATED_FULLY }
注销控制器:
use App\Entity\RefreshToken;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
use Symfony\Component\Security\Csrf\TokenStorage\TokenStorageInterface as CsrfTokenStorageInterface;
class LogoutController extends AbstractController
{
public function __construct(
private RequestStack $requestStack,
private TokenStorageInterface $tokenStorage,
private CsrfTokenStorageInterface $csrfTokenStorage,
private EntityManagerInterface $entityManager
) {
}
#[Route('/api/logout', name: 'app_logout')]
public function logout(): Response
{
$token = $this->tokenStorage->getToken();
if ($token !== null) {
$this->tokenStorage->setToken(null);
}
$csrfTokenId = $this->requestStack->getCurrentRequest()->get('_csrf_token_id');
if ($csrfTokenId !== null) {
$this->csrfTokenStorage->removeToken($csrfTokenId);
}
$tokens = $this->entityManager->getRepository(RefreshToken::class)->findBy([
'username' => $token->getUserIdentifier()
]);
foreach ($tokens as $item) {
$this->entityManager->remove($item);
}
$this->entityManager->flush();
return new Response();
}
}
用户实体:
#[ApiResource(
operations: [
new Get(),
new Get(name: 'current_user', uriTemplate: '/user'),
],
normalizationContext: ['groups' => ['api']],
denormalizationContext: ['groups' => ['api']]
)]
#[ORM\Entity(repositoryClass: UserRepository::class)]
class User implements UserInterface, PasswordAuthenticatedUserInterface
{
...
}
RefreshToken实体:
#[ORM\Entity]
#[ORM\Table(name: 'refresh_tokens')]
class RefreshToken extends BaseRefreshToken
{
}
苏。我创建了获取当前登录用户的端点,然后通过使用实体操作的过滤器将其拉出。
final class CurrentUserExtension implements QueryItemExtensionInterface
{
public function __construct(private readonly TokenStorageInterface $tokenStorage)
{
}
public function applyToItem(
QueryBuilder $queryBuilder,
QueryNameGeneratorInterface $queryNameGenerator,
string $resourceClass,
array $identifiers,
Operation $operation = null,
array $context = []
): void {
if (User::class === $resourceClass && 'current_user' === $operation->getName() && $this->tokenStorage->getToken()) {
$this->support($queryBuilder);
}
}
private function support(QueryBuilder $queryBuilder): void
{
$user = $this->tokenStorage->getToken()->getUserIdentifier();
$rootAlias = $queryBuilder->getRootAliases()[0];
$queryBuilder->andWhere(sprintf('%s.email = :current_user_email', $rootAlias));
$queryBuilder->setParameter('current_user_email', $user);
}
}
注销有效。我想是这样。但是在发送获取当前用户的请求后,我仍然得到它,但我不应该得到它,因为我已经注销了。 此外,我将令牌转储到扩展中,它仍然存在,尽管它不应该存在。 新令牌也会添加到refresh_token 表中。 该怎么办?怎样生活?
我认为不可能撤销 JWT,因为没有数据库用于存储它,并且它会一直存在直到过期。
要撤销刷新令牌,您应该执行:
php bin/console gesdinet:jwt:revoke TOKEN