更改后重新加载用户角色,无需重新登录

时间:2019-03-18 12:04:25

标签: symfony symfony4 symfony-security

如何刷新登录的用户角色,例如管理员用户何时更改过?我找到了always_authenticate_before_granting安全选项(Symfony 4文档中未包含)并将其设置为true

security.yaml:

security:
    always_authenticate_before_granting: true
    encoders:
        App\Entity\Main\User:
            algorithm: bcrypt

    providers:
        app_user_provider:
            entity:
                class: App\Entity\Main\User
                property: email
    role_hierarchy:
        ROLE_ADMIN:       ROLE_USER
    firewalls:
        dev:
            pattern: ^/(_(profiler|wdt)|css|images|js)/
            security: false
        main:
            anonymous: ~
            guard:
                authenticators:
                    - App\Security\LoginFormAuthenticator
            form_login:
                login_path: login
                check_path: login
            logout:
                path: logout
                target: homepage
            remember_me:
                secret:   '%kernel.secret%'
                path:     /
    access_control:
        - { path: ^/login$, roles: IS_AUTHENTICATED_ANONYMOUSLY }
        - { path: ^/.*, roles: ROLE_USER }

但没有生效。

更新

我已经创建了onRequest订户:

class RequestSubscriber implements EventSubscriberInterface
{
    private $tokenStorage;

    public function __construct(TokenStorageInterface $tokenStorage)
    {
        $this->tokenStorage = $tokenStorage;
    }

    public static function getSubscribedEvents(): array
    {
        return [
            KernelEvents::REQUEST => 'onRequest'
        ];
    }

    public function onRequest(GetResponseEvent $event): void
    {
        if (!$event->isMasterRequest()) {
            return;
        }

        if(!$token = $this->tokenStorage->getToken()) return;

        $sessionUser = $token->getUser();

        if ($sessionUser instanceof User) {
            $this->tokenStorage->setToken(new PostAuthenticationGuardToken($sessionUser, 'main', $sessionUser->getRoles()));
        }
    }
}

现在我可以在每个请求上刷新更新的角色,但是将sessionUser与databaseUser进行比较是没有意义的,因为sessionUser始终包含新更新的角色,尽管在Symfony Profiler>安全令牌中列出了旧角色(以防万一,当然,不要设置新令牌。

1 个答案:

答案 0 :(得分:1)

Tl; dr恐怕您必须引入自己的机制才能使这项工作生效。

会话令牌存储在用户会话中。这将对您的应用程序的性能产生很大的影响,因为每次都需要调用数据库以检查角色是否已更改。

因此,您将需要一个request listener,它将数据库角色与当前用户角色进行比较,如果不相同,则用新的角色列表替换会话中的令牌,例如。 (伪代码):

sessionUser = tokenStorage.token.user
databaseUser = userRepository.find(sessionUser.id)

if sessionUser.roles !== databaseUser.roles
    tokenStorage.setToken(new PostAuthenticationGuardToken(…))

或将高速缓存用作标志载体,以将更改通知用户。当然,这种方法会更快。

sessionUser = tokenStorage.token.user

// of course the flag has to be set when the user's role gets changed
cacheToken = 'users.role_reload.' . sessionUser.id

if cache.has(cacheToken)
    databaseUser = userRepository.find(sessionUser.id)
    tokenStorage.setToken(new PostAuthenticationGuardToken(…))
    cache.remove(cacheToken)

无论哪种方式,用户都必须在每次请求时要求应用程序进行角色更改。