如果用户的状态为非活动,则阻止用户登录
Prevent user from logging in if his status is inactive
我试图阻止用户登录,他的状态是不活跃的。我正在使用 API-Platform 和 LexikJWT 包。
我试图通过扩展 JWTTokenAuthenticator->checkCredentials
来创建一个 JWTAuthentication
守卫,但问题是这在用户已经登录后才有效。
我想要实现的是 return 向用户发送一条他需要先激活帐户的消息,或者任何其他消息,最好是任何自定义条件下的任何自定义消息。
我的安全 YAML 如下所示:
security:
encoders:
App\Entity\User:
algorithm: bcrypt
providers:
app_user_provider:
entity:
class: App\Entity\User
property: email
firewalls:
dev:
pattern: ^/_(profiler|wdt)
security: false
api:
pattern: ^/api/
stateless: true
anonymous: true
provider: app_user_provider
json_login:
check_path: /api/authentication_token
username_path: email
password_path: password
success_handler: lexik_jwt_authentication.handler.authentication_success
failure_handler: lexik_jwt_authentication.handler.authentication_failure
guard:
authenticators:
- app.jwt_token_authenticator
main:
anonymous: true
access_control:
- { path: ^/api/authentication_token, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/api/graphql, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/public-api, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/api/, roles: [ROLE_MANAGER, ROLE_LEADER] }
- { path: ^/, roles: IS_AUTHENTICATED_ANONYMOUSLY }
服务:
services:
# default configuration for services in *this* file
_defaults:
autowire: true # Automatically injects dependencies in your services.
autoconfigure: true # Automatically registers your services as commands, event subscribers, etc.
# makes classes in src/ available to be used as services
# this creates a service per class whose id is the fully-qualified class name
App\:
resource: '../src/*'
exclude: '../src/{DependencyInjection,Entity,Migrations,Tests,Kernel.php}'
# controllers are imported separately to make sure services can be injected
# as action arguments even if you don't extend any base controller class
App\Controller\:
resource: '../src/Controller'
tags: ['controller.service_arguments']
# add more service definitions when explicit configuration is needed
# please note that last definitions always *replace* previous ones
gedmo.listener.softdeleteable:
class: Gedmo\SoftDeleteable\SoftDeleteableListener
tags:
- { name: doctrine.event_subscriber, connection: default }
calls:
- [ setAnnotationReader, [ '@annotation_reader' ] ]
acme_api.event.authentication_success_listener:
class: App\EventListener\AuthenticationSuccessListener
tags:
- { name: kernel.event_listener, event: lexik_jwt_authentication.on_authentication_success, method: onAuthenticationSuccessResponse }
app.jwt_token_authenticator:
autowire: false
autoconfigure: false
class: App\Security\Guard\JWTTokenAuthenticator
parent: lexik_jwt_authentication.security.guard.jwt_token_authenticator
'App\Serializer\ApiNormalizer':
decorates: 'api_platform.serializer.normalizer.item'
arguments: ['@App\Serializer\ApiNormalizer.inner', '@doctrine.orm.entity_manager']
'App\Serializer\HydraApiNormalizer':
decorates: 'api_platform.jsonld.normalizer.item'
arguments: ['@App\Serializer\ApiNormalizer.inner', '@doctrine.orm.entity_manager']
'App\Voter\ModifyUserVoter':
public: false
tags:
- { name: security.voter }
身份验证器守卫
class JWTTokenAuthenticator extends BaseAuthenticator
{
/**
* {@inheritdoc}
*/
public function checkCredentials($credentials, UserInterface $user)
{
if (!$user->getRoles() || !in_array($user->getRoles()[0], ['ROLE_MANAGER', 'ROLE_LEADER'])) {
throw new UnauthorizedHttpException(rand(10000, 99999), 'Unauthorized');
}
if (!$user->getStatus() != "active") {
throw new UnauthorizedHttpException(rand(10000, 99999), 'Unauthorized');
}
return true;
}
}
我已经通过在 User
实体上实现 AdvancedUserInterface
而不是 UserInterface
并向 isEnabled()
方法添加逻辑来实现我想要的。
您需要创建 UserCheckerInterface
的实现。 (Docs)
例如,看这个:
use Symfony\Component\Security\Core\Exception\DisabledException;
use Symfony\Component\Security\Core\User\UserCheckerInterface;
use Symfony\Component\Security\Core\User\UserInterface;
class EasyUserChecker implements UserCheckerInterface
{
public function checkPreAuth(UserInterface $user): void
{
// my checker only cares for our managed user classes, we return with no action
if (!$user instanceof AppAdmin && !$user instanceof AppUser) {
return;
}
// our user entities can be deleted or disabled. If the user is neither, we return with no action
if (!$user->isDeleted() && !empty($user->isEnabled())) {
return;
}
// if we got here, we throw an exception
throw new DisabledException('User account is disabled.');
}
// I'm not using the post authorization check, but needs to have an implementation to satisfy the interface.
public function checkPostAuth(UserInterface $user): void
{
}
}
您在安全配置中启用了检查器。例如:
security:
firewalls:
api:
pattern: ^/api
user_checker: App\Security\EasyChecker
现在你不应该编写 AdvancedUserInterface
的新实现。使用它作为解决方案是错误的方法。
该接口 is deprecated since 4.1,并且在 Symfony 5 中完全 删除了 。因此,依赖该接口的代码将无法升级到更新的 Symfony 版本。
我试图阻止用户登录,他的状态是不活跃的。我正在使用 API-Platform 和 LexikJWT 包。
我试图通过扩展 JWTTokenAuthenticator->checkCredentials
来创建一个 JWTAuthentication
守卫,但问题是这在用户已经登录后才有效。
我想要实现的是 return 向用户发送一条他需要先激活帐户的消息,或者任何其他消息,最好是任何自定义条件下的任何自定义消息。
我的安全 YAML 如下所示:
security:
encoders:
App\Entity\User:
algorithm: bcrypt
providers:
app_user_provider:
entity:
class: App\Entity\User
property: email
firewalls:
dev:
pattern: ^/_(profiler|wdt)
security: false
api:
pattern: ^/api/
stateless: true
anonymous: true
provider: app_user_provider
json_login:
check_path: /api/authentication_token
username_path: email
password_path: password
success_handler: lexik_jwt_authentication.handler.authentication_success
failure_handler: lexik_jwt_authentication.handler.authentication_failure
guard:
authenticators:
- app.jwt_token_authenticator
main:
anonymous: true
access_control:
- { path: ^/api/authentication_token, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/api/graphql, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/public-api, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/api/, roles: [ROLE_MANAGER, ROLE_LEADER] }
- { path: ^/, roles: IS_AUTHENTICATED_ANONYMOUSLY }
服务:
services:
# default configuration for services in *this* file
_defaults:
autowire: true # Automatically injects dependencies in your services.
autoconfigure: true # Automatically registers your services as commands, event subscribers, etc.
# makes classes in src/ available to be used as services
# this creates a service per class whose id is the fully-qualified class name
App\:
resource: '../src/*'
exclude: '../src/{DependencyInjection,Entity,Migrations,Tests,Kernel.php}'
# controllers are imported separately to make sure services can be injected
# as action arguments even if you don't extend any base controller class
App\Controller\:
resource: '../src/Controller'
tags: ['controller.service_arguments']
# add more service definitions when explicit configuration is needed
# please note that last definitions always *replace* previous ones
gedmo.listener.softdeleteable:
class: Gedmo\SoftDeleteable\SoftDeleteableListener
tags:
- { name: doctrine.event_subscriber, connection: default }
calls:
- [ setAnnotationReader, [ '@annotation_reader' ] ]
acme_api.event.authentication_success_listener:
class: App\EventListener\AuthenticationSuccessListener
tags:
- { name: kernel.event_listener, event: lexik_jwt_authentication.on_authentication_success, method: onAuthenticationSuccessResponse }
app.jwt_token_authenticator:
autowire: false
autoconfigure: false
class: App\Security\Guard\JWTTokenAuthenticator
parent: lexik_jwt_authentication.security.guard.jwt_token_authenticator
'App\Serializer\ApiNormalizer':
decorates: 'api_platform.serializer.normalizer.item'
arguments: ['@App\Serializer\ApiNormalizer.inner', '@doctrine.orm.entity_manager']
'App\Serializer\HydraApiNormalizer':
decorates: 'api_platform.jsonld.normalizer.item'
arguments: ['@App\Serializer\ApiNormalizer.inner', '@doctrine.orm.entity_manager']
'App\Voter\ModifyUserVoter':
public: false
tags:
- { name: security.voter }
身份验证器守卫
class JWTTokenAuthenticator extends BaseAuthenticator
{
/**
* {@inheritdoc}
*/
public function checkCredentials($credentials, UserInterface $user)
{
if (!$user->getRoles() || !in_array($user->getRoles()[0], ['ROLE_MANAGER', 'ROLE_LEADER'])) {
throw new UnauthorizedHttpException(rand(10000, 99999), 'Unauthorized');
}
if (!$user->getStatus() != "active") {
throw new UnauthorizedHttpException(rand(10000, 99999), 'Unauthorized');
}
return true;
}
}
我已经通过在 User
实体上实现 AdvancedUserInterface
而不是 UserInterface
并向 isEnabled()
方法添加逻辑来实现我想要的。
您需要创建 UserCheckerInterface
的实现。 (Docs)
例如,看这个:
use Symfony\Component\Security\Core\Exception\DisabledException;
use Symfony\Component\Security\Core\User\UserCheckerInterface;
use Symfony\Component\Security\Core\User\UserInterface;
class EasyUserChecker implements UserCheckerInterface
{
public function checkPreAuth(UserInterface $user): void
{
// my checker only cares for our managed user classes, we return with no action
if (!$user instanceof AppAdmin && !$user instanceof AppUser) {
return;
}
// our user entities can be deleted or disabled. If the user is neither, we return with no action
if (!$user->isDeleted() && !empty($user->isEnabled())) {
return;
}
// if we got here, we throw an exception
throw new DisabledException('User account is disabled.');
}
// I'm not using the post authorization check, but needs to have an implementation to satisfy the interface.
public function checkPostAuth(UserInterface $user): void
{
}
}
您在安全配置中启用了检查器。例如:
security:
firewalls:
api:
pattern: ^/api
user_checker: App\Security\EasyChecker
现在你不应该编写 AdvancedUserInterface
的新实现。使用它作为解决方案是错误的方法。
该接口 is deprecated since 4.1,并且在 Symfony 5 中完全 删除了 。因此,依赖该接口的代码将无法升级到更新的 Symfony 版本。