0

我正在建立一个网站,我想为前端和后端使用单独的防火墙和身份验证系统。所以我security.yml的配置如下。我在早期开发阶段使用 in_memory 用户提供程序。

security:
    encoders:
        Symfony\Component\Security\Core\User\User: plaintext

    role_hierarchy:
        ROLE_ADMIN:       ROLE_USER
        ROLE_SUPER_ADMIN: [ROLE_USER, ROLE_ADMIN, ROLE_ALLOWED_TO_SWITCH]

    providers:
        backend_in_memory:
            memory:
                users:
                    admin: { password: admin, roles: [ 'ROLE_ADMIN' ] }
        frontend_in_memory:
            memory:
                users:
                    user:  { password: 12345, roles: [ 'ROLE_USER' ] }

    firewalls:

        # (Configuration for backend omitted)

        frontend_login_page:
            pattern:  ^/login$
            security: false

        frontend:
            pattern:   ^/
            provider: frontend_in_memory
            anonymous: ~
            form_login:
                check_path: login_check_route  # http://example.com/login_check
                login_path: login_route        # http://example.com/login


    access_control:
        # (Configuration for backend omitted)
        - { path: ^/login, roles: IS_AUTHENTICATED_ANONYMOUSLY }
        - { path: ^/, roles: ROLE_USER }

我省略了后端部分,因为它无关紧要。当省略的部分被注释掉时,问题仍然存在。

问题是前端身份验证不适用于上述配置。这是我所做的:

  1. 访问http://example.com/login
  2. 输入凭证(user:12345),点击登录
  3. http://example.com/login_check对用户进行身份验证
  4. 身份验证服务将用户重定向回http://example.com/。没有错误被抛出。事实上,当我打开 debug_redirects 选项时,它清楚地表明“用户”在重定向页面上通过了身份验证。

预期行为:安全令牌应显示我在重定向后以“用户”身份登录并返回索引页面。

实际行为:安全令牌在重定向后仍然显示“匿名”登录并返回索引页面。

但是使用几乎相同的设置(路径和路由名称不同),后端部分可以正常工作。

经过一番调查,我发现原因是用户提供程序当前的编写方式。请注意,frontend_in_memory部分位于用于后端身份验证的backend_in_memory下方。所以我为前端防火墙明确指定了frontend_in_memory提供程序。它的工作原理 - 我必须在前端登录页面中使用“用户:12345”登录。使用“admin”登录将不起作用。所以它必须使用正确的用户提供程序。但我怀疑框架无法正确更新安全令牌,因为它仍在从第一个用户提供程序(即backend_in_memory )搜索“用户”帐户。事实上,我可以通过以下任一更改使上述配置工作:

  1. 将“用户”登录添加到backend_in_memory提供者的用户列表(密码不必相同),或
  2. 将frontend_in_memorybackend_in_memory交换,以便frontend_in_memory成为第一个用户提供者。

当然,它们不是解决这个问题的正确方法。将“用户”帐户添加到后端根本没有意义;交换两个用户提供者的顺序修复了前端但破坏了后端。

我想知道出了什么问题以及如何解决这个问题。谢谢!

4

1 回答 1

0

当我发布问题时我被卡住了,但是在睡觉后找到了答案;)

原来我遇到了很久以前报告的问题: https ://github.com/symfony/symfony/issues/4498

简而言之,

  • 问题不在于配置。
  • 这也与身份验证无关。
  • 它实际上与重定向后如何刷新经过身份验证的用户有关。这就是为什么应用程序在重定向页面上被正确地认证为“用户”,但在那之后却没有。

这是框架刷新用户时的代码(可以在\Symfony\Component\Security\Http\Firewall\ContextListener中找到):

    foreach ($this->userProviders as $provider) {
        try {
            $refreshedUser = $provider->refreshUser($user);
            $token->setUser($refreshedUser);

            if (null !== $this->logger) {
                $this->logger->debug(sprintf('Username "%s" was reloaded from user provider.', $refreshedUser->getUsername()));
            }

            return $token;
        } catch (UnsupportedUserException $unsupported) {
            // let's try the next user provider // *1
        } catch (UsernameNotFoundException $notFound) {
            if (null !== $this->logger) {
                $this->logger->warning(sprintf('Username "%s" could not be found.', $notFound->getUsername()));
            }

            return; // *2
        }
    }

上面的代码显示了框架如何循环通过用户提供者来找到特定的用户 ( refreshUser())。*1 和 *2 是我添加的。如果用户提供者抛出一个 UnsupportedUserException,这意味着提供者不对提供的 负责UserInterface。然后侦听器将迭代到下一个用户提供程序 (*1)。

但是,如果用户provider抛出的是a UsernameNotFoundException,这意味着provider负责提供的UserInterface,但是找不到对应的账号。然后循环将立即停止。(*2)

\Symfony\Component\Security\Core\User\InMemoryUserProvider在我的问题中,前端和后端环境都使用相同的用户提供程序。并InMemoryUserProvider负责UserInterface实施Symfony\Component\Security\Core\User\User

在前端,“用户”实际上已成功通过身份验证。但是,在用户刷新尝试中,

  • 用户提供者的顺序是这样的:后端内存提供者,前端内存提供者。
  • 因此,后端内存提供程序将首先运行。
  • 后端内存提供者认为它对提供的内容负责, UserInterface因为它也是Symfony\Component\Security\Core\User\User.
  • 但它无法找到“用户”帐户(它只有“管理员”帐户)。
  • 然后它抛出一个UsernameNotFoundException.
  • refreshUser()例程不会费心尝试下一个提供者,因为这UsernameNotFoundException意味着已经找到了负责的用户提供者。相反,它会停止尝试并删除身份验证令牌。

这解释了为什么配置不起作用。尽管使用了不同的用户提供程序,但解决此问题的唯一方法是复制框架InMemoryUserProviderUser类并更改refreshUser()方法以检查复制的User类,以便前端和后端用户提供程序使用不同的用户类并且不会发生冲突。

于 2014-11-27T04:10:40.173 回答