用户角色何时刷新以及如何强制它?

net*_*key 21 security roles symfony symfony-2.1 symfony-security

首先,我没有使用FOSUserBundle,我不能,因为我正在移植一个遗留系统,它有自己的模型层(没有Doctrine/Mongo /无论在这里)和其他非常自定义的行为.

我正在尝试将我的遗留角色系统与Symfony连接,因此我可以在控制器和视图中使用本机symfony安全性.

我的第一次尝试加载并返回该用户的所有角色在getRoles()从方法Symfony\Component\Security\Core\User\UserInterface.起初,它看起来像是有效的.但在深入了解之后,我注意到这些角色只有在用户登录时才会刷新.这意味着如果我从用户授予或撤消角色,他将必须注销并重新登录才能使更改生效.但是,如果我从用户撤消安全角色,我希望立即应用该角色,这样我就无法接受这种行为.

我希望Symfony做的是在每个请求上重新加载用户的角色,以确保它们是最新的.我已经实现了一个自定义用户提供程序,并且refreshUser(UserInterface $user)每次请求都会调用其方法,但不会以某种方式刷新角色.

在我的UserProvider中加载/刷新用户的代码如下所示:

public function loadUserByUsername($username) {
    $user = UserModel::loadByUsername($username); // Loads a fresh user object including roles!
    if (!$user) {
        throw new UsernameNotFoundException("User not found");
    }
    return $user;
}
Run Code Online (Sandbox Code Playgroud)

(refreshUser看起来很相似)

有没有办法让Symfony在每个请求上刷新用户角色?

net*_*key 21

因此,经过几天努力寻找可行的解决方案并为Symfony2用户邮件列表做出贡献,我终于找到了它.以下内容源自https://groups.google.com/d/topic/symfony2/NDBb4JN3mNc/discussion上的讨论

事实证明,有一个接口Symfony\Component\Security\Core\User\EquatableInterface不是用于比较对象标识,而是用于比较

测试两个对象在安全性和重新认证上下文中是否相等

在您的用户类中实现该接口(已经实现的接口UserInterface).实现唯一必需的方法,isEqualTo(UserInterface $user)以便在当前用户的角色与传递的用户的角色不同时返回false.

注意:User对象在会话中序列化.由于序列化的工作方式,请确保将角色存储在用户对象的字段中,而不是直接在getRoles()Method中检索它们,否则所有这些都不起作用!

以下是特定方法的示例:

protected $roles = null;

public function getRoles() {

    if ($this->roles == null) {
        $this->roles = ...; // Retrieve the fresh list of roles
                            // from wherever they are stored here
    }

    return $this->roles;
}

public function isEqualTo(UserInterface $user) {

    if ($user instanceof YourUserClass) {
        // Check that the roles are the same, in any order
        $isEqual = count($this->getRoles()) == count($user->getRoles());
        if ($isEqual) {
            foreach($this->getRoles() as $role) {
                $isEqual = $isEqual && in_array($role, $user->getRoles());
            }
        }
        return $isEqual;
    }

    return false;
}
Run Code Online (Sandbox Code Playgroud)

另请注意,当角色实际更改并重新加载页面时,探查器工具栏可能会告诉您未对用户进行身份验证.另外,查看分析器,您可能会发现角色实际上没有刷新.

我发现刷新的角色确实有效.只是如果没有命中授权约束(没有@Secure注释,防火墙中没有必需的角色等),则实际上不会刷新并且用户保持在"未经身份验证"状态.

只要您点击执行任何类型授权检查的页面,用户角色就会刷新,并且探查器工具栏会显示带有绿点的用户,并再次显示"已验证:是".

这对我来说是可接受的行为 - 希望它有用:)

  • 你可以将主要部分写在一行中 `return count($this->getRoles()) == count($user->getRoles()) && count(array_diff($this->getRoles(), $user-> getRoles())) == 0;` (2认同)

Mar*_*arc 11

在您的security.yml(或替代方案)中:

security:
    always_authenticate_before_granting: true
Run Code Online (Sandbox Code Playgroud)

我生命中最轻松的游戏.


Sim*_*amp 7

在Controller中,将角色添加到用户并保存到数据库后,只需调用:

// Force refresh of user roles
$token = $this->get('security.context')->getToken()->setAuthenticated(false);
Run Code Online (Sandbox Code Playgroud)

  • 对我来说很有用,但是如果你想在较新版本的Symfony中避免弃用通知,请使用`security.token_storage`而不是`security.context` :) (4认同)