如果用户状态为非活动状态,则阻止用户登录

Posted

技术标签:

【中文标题】如果用户状态为非活动状态,则阻止用户登录【英文标题】:Prevent user from logging in if his status is inactive 【发布时间】:2020-06-06 00:08:46 【问题描述】:

我试图阻止用户登录他的状态是非活动的。我正在使用带有 LexikJWT 捆绑包的 API 平台。

我试图通过扩展JWTTokenAuthenticator->checkCredentials 来创建JWTAuthentication 保护,但问题是这在用户已经登录后才有效。

我想要实现的是向用户返回一条消息,他需要首先激活他的帐户,或者任何其他消息,最好是在任何自定义条件下的任何自定义消息。

我的安全 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;
    

【问题讨论】:

【参考方案1】:

通过在User 实体上实现AdvancedUserInterface 而不是UserInterface 并向isEnabled() 方法添加逻辑,我设法实现了我想要的。

【讨论】:

AdvancedUserInterface 自 4.1 起已弃用,并在 5 上删除。检查其他答案以向前兼容的方式进行。【参考方案2】:

您需要创建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 版本。

【讨论】:

谢谢,我会在工作中尝试一下,我会接受你的回答。也谢谢你的样品和解释我真的很感激 谢谢,这按预期工作。我已将 AdvancedUserInterface 替换为 UserInterace。一件小事,登录后我更改了用户的状态,以查看用户登录时的行为,突然他被禁用,然后它可以工作,但它不会像它一样返回传递给 DisabledException 的错误消息登录时,它会返回一些默认消息,我猜有些东西会覆盖它,知道什么或在哪里吗?

以上是关于如果用户状态为非活动状态,则阻止用户登录的主要内容,如果未能解决你的问题,请参考以下文章

Django 仅使用电子邮件创建用户并设置为非活动状态

根据用户的角色和状态登录laravel

使用 React Context 阻止登录状态在主页中更改

Laravel 5.4 检查用户是不是处于活动状态,如果没有则回复消息

仅当用户在 Laravel 5.7 中处于活动状态时才登录用户

php 社区活动(CE):如果用户已登录,请将发布状态设置为已发布,否则回退到CE设置的默认值。