Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/security/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Security 何时刷新用户角色以及如何强制刷新?_Security_Symfony_Roles_Symfony 2.1_Symfony Security - Fatal编程技术网

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

Security 何时刷新用户角色以及如何强制刷新?,security,symfony,roles,symfony-2.1,symfony-security,Security,Symfony,Roles,Symfony 2.1,Symfony Security,首先,我没有使用FOSUserBundle,我不能,因为我移植的是一个遗留系统,它有自己的模型层(这里没有条令/Mongo/任何东西)和其他非常定制的行为 我正在尝试将我的旧角色系统与Symfony连接,以便在控制器和视图中使用本机Symfony安全性 我的第一次尝试是从Symfony\Component\Security\Core\user\UserInterface加载并返回getRoles()方法中的所有用户角色。起初,这看起来是可行的。但在深入研究之后,我注意到这些角色只有在用户登录时才

首先,我没有使用FOSUserBundle,我不能,因为我移植的是一个遗留系统,它有自己的模型层(这里没有条令/Mongo/任何东西)和其他非常定制的行为

我正在尝试将我的旧角色系统与Symfony连接,以便在控制器和视图中使用本机Symfony安全性

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

我想要Symfony做的是在每个请求上重新加载用户的角色,以确保它们是最新的。我已经实现了一个自定义用户提供程序,它的
refreshUser(UserInterface$user)
方法在每个请求上都被调用,但是角色不知何故没有被刷新

在my 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;
}
refreshUser
看起来很相似)


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

我通过实现自己的EntityUserProvider并重写loadByUsername($username)方法来实现此行为:


诀窍是每次调用
loadByUsername
时调用
setRoles
。。。希望它能有所帮助

因此,经过几天的努力,我终于找到了一个可行的解决方案,并为Symfony2用户邮件列表做出了贡献。以下内容来源于

事实证明,有一个接口
Symfony\Component\Security\Core\User\equalableinterface
,它不是用来比较对象标识的,而是用来精确地比较对象标识的

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

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

注意:用户对象在会话中序列化。由于序列化的工作方式,请确保将角色存储在用户对象的字段中,并且不要直接在
getRoles()
方法中检索它们,否则所有这些都无法工作

下面是一个具体方法的示例:

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;
}
另外,请注意,当角色实际发生更改并重新加载页面时,探查器工具栏可能会告诉您用户未通过身份验证。另外,查看探查器,您可能会发现角色实际上没有得到刷新

我发现提神这个角色确实有效。只是,如果没有达到授权约束(没有
@Secure
注释,防火墙中没有必需的角色等),则实际上不会进行刷新,用户将保持在“未经验证”状态

只要您点击一个执行任何类型授权检查的页面,就会刷新用户角色,并且探查器工具栏会再次显示一个绿点和“Authenticated:yes”的用户


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

解决方案是在更新事件上挂起订阅者。若更新的实体是用户,和登录的用户相同,那个么我使用AuthenticationManager服务进行身份验证。当然,您必须将服务容器(或相关服务)注入订户。我更喜欢注入整个容器以防止循环引用问题

public function postUpdate(LifecycleEventArgs $ev) {
    $entity = $ev->getEntity();

    if ($entity instanceof User) {
        $sc = $this->container->get('security.context');
        $user = $sc->getToken()->getUser();

        if ($user === $entity) {
            $token = $this->container->get('security.authentication.manager')->authenticate($sc->getToken());

            if ($token instanceof TokenInterface) {
                $sc->setToken($token);
            }
        }
    }
}

从控制器向用户添加角色并保存到数据库后,只需调用:

// Force refresh of user roles
$token = $this->get('security.context')->getToken()->setAuthenticated(false);
看一看,在您的security.yml中的
security.yml

中将
始终在授予
之前进行身份验证设置为
true


我一生中最简单的游戏。

对不起,我不能在评论中回复,所以我重播了这个问题。如果symfony security中的新手尝试在自定义密码身份验证中获得角色刷新工作,则函数内部身份验证停止:

if(count($token->getRoles()) > 0 ){
        if ($token->getUser() == $user ){
            $passwordValid=true;
        }
    }

不要检查DB/LDAP或任何地方的密码。若用户进入系统,那个么$token中的用户只是用户名,并没有角色。

我一直在为Symfony4进行斗争,我想我终于找到了解决方案

在我的例子中,角色取决于用户所在的“公司”。它可能是一家公司的CEO,但另一家公司的操作员,菜单、权限等取决于公司。切换公司时,用户不得重新登录

最后,我完成了以下工作:

  • 将防火墙设置为无状态
  • 在FormAuthentication类中,我在会话中使用用户名明确地设置了一个属性
  • 我设置了另一个守卫,它基本上接受这个属性,并为每个请求从数据库中为它加载用户
SessionAuthenticator(返回JSON,您可能需要调整它):

最后,我的security.yaml:

main:
            anonymous:
            stateless: true
            guard:
                entry_point: App\Security\FormAuthenticator
                authenticators:
                    - App\Security\SessionAuthenticator
                    - App\Security\FormAuthenticator
工作很好。我可以在工具栏中看到更改,角色被刷新

嗯,


Esteve

这一解决方案似乎必须遵守原则。但我并没有使用教义。此外,不管原则如何,我看不出在用户实体上设置属性会如何影响Symfony的安全上下文中的内容?用户对象在令牌中设置,并在用于令牌的持久层(会话/cookies/bdd)中检索。如果您没有在每次刷新用户时专门刷新角色,那么安全上下文将使用令牌角色(通过PHP会话持久化)。让我们看一看DaoAuthenticationProvider::retir
if(count($token->getRoles()) > 0 ){
        if ($token->getUser() == $user ){
            $passwordValid=true;
        }
    }
class FormAuthenticator extends AbstractFormLoginAuthenticator
{
    /** Constructor omitted */

    public function supports(Request $request)
    {
        return 'app_login' === $request->attributes->get('_route')
            && $request->isMethod('POST');
    }

    public function getCredentials(Request $request)
    {
        $credentials = [
            'nomusuari' => $request->request->get('nomusuari'),
            'password' => $request->request->get('password'),
            'csrf_token' => $request->request->get('_csrf_token'),
        ];
        $request->getSession()->set(
            Security::LAST_USERNAME,
            $credentials['nomusuari']
        );

        return $credentials;
    }

    public function getUser($credentials, UserProviderInterface $userProvider)
    {
        $token = new CsrfToken('authenticate', $credentials['csrf_token']);
        if (!$this->csrfTokenManager->isTokenValid($token)) {
            throw new InvalidCsrfTokenException();
        }

        $user = $userProvider->loadUserByUsername($credentials['nomusuari']);

        if (!$user) {
            // fail authentication with a custom error
            throw new CustomUserMessageAuthenticationException('Invalid user/password');
        }

        return $user;
    }

    public function checkCredentials($credentials, UserInterface $user)
    {
        $valid = $this->passwordEncoder->isPasswordValid($user, $credentials['password']);
        return $valid;
    }

    public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey)
    {
        $request->getSession()->set("user_username",$token->getUsername());

        return new RedirectResponse(
          $this->urlGenerator->generate("main")
        );
    }

    protected function getLoginUrl()
    {
        return $this->urlGenerator->generate('app_login');
    }
}
class SessionAuthenticator extends AbstractGuardAuthenticator
{
    /**
     * Called on every request to decide if this authenticator should be
     * used for the request. Returning `false` will cause this authenticator
     * to be skipped.
     */
    public function supports(Request $request)
    {
        return $request->getSession()->has("user_username");
    }

    /**
     * Called on every request. Return whatever credentials you want to
     * be passed to getUser() as $credentials.
     */
    public function getCredentials(Request $request)
    {
        return $request->getSession()->get("user_username","");
    }

    public function getUser($credentials, UserProviderInterface $userProvider)
    {
        if (null === $credentials) {
            // The token header was empty, authentication fails with HTTP Status
            // Code 401 "Unauthorized"
            return null;
        }

        // if a User is returned, checkCredentials() is called
        /*return $this->em->getRepository(User::class)
            ->findOneBy(['apiToken' => $credentials])
        ;*/
        return $userProvider->loadUserByUsername($credentials);
    }

    public function checkCredentials($credentials, UserInterface $user)
    {
        // Check credentials - e.g. make sure the password is valid.
        // In case of an API token, no credential check is needed.

        // Return `true` to cause authentication success
        return true;
    }

    public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey)
    {
        // on success, let the request continue
        return null;
    }

    public function onAuthenticationFailure(Request $request, AuthenticationException $exception)
    {
        $data = [
            // you may want to customize or obfuscate the message first
            'message' => strtr($exception->getMessageKey(), $exception->getMessageData())

            // or to translate this message
            // $this->translator->trans($exception->getMessageKey(), $exception->getMessageData())
        ];

        return new JsonResponse($data, Response::HTTP_UNAUTHORIZED);
    }

    /**
     * Called when authentication is needed, but it's not sent
     */
    public function start(Request $request, AuthenticationException $authException = null)
    {
        $data = [
            // you might translate this message
            'message' => 'Authentication Required'
        ];

        return new JsonResponse($data, Response::HTTP_UNAUTHORIZED);
    }

    public function supportsRememberMe()
    {
        return false;
    }
}
main:
            anonymous:
            stateless: true
            guard:
                entry_point: App\Security\FormAuthenticator
                authenticators:
                    - App\Security\SessionAuthenticator
                    - App\Security\FormAuthenticator