0

フロントエンドとバックエンドで別々のファイアウォールと認証システムを使用したい Web サイトをセットアップしています。だから私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プロバイダーを明示的に指定します。そして、それはうまくいきます-フロントエンドのログインページで「user:12345」でログインする必要があります。「admin」でログインしても機能しません。したがって、正しいユーザー プロバイダーを使用している必要があります。しかし、 backend_in_memoryである最初のユーザープロバイダーから「ユーザー」アカウントをまだ検索しているため、フレームワークがセキュリティトークンを正しく更新できないと思われます。実際、次の変更のいずれかを使用して、上記の構成を機能させることができます。

  1. backend_in_memoryプロバイダーのユーザー リストに「ユーザー」ログインを追加します(パスワードは同じである必要はありません)。
  2. frontend_in_memorybackend_in_memoryと交換して、frontend_in_memoryが最初のユーザー プロバイダーになるようにします。

もちろん、それらはこの問題を解決する正しい方法ではありません。バックエンドに「ユーザー」アカウントを追加しても意味がありません。2 つのユーザー プロバイダーの順序を入れ替えると、フロントエンドは修正されますが、バックエンドは壊れます。

何が問題なのか、これを修正する方法を知りたいです。ありがとうございました!

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) に反復します。

ただし、ユーザー プロバイダーがスローしたのが であるUsernameNotFoundException場合、これはプロバイダー提供された を担当している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代わりに、試行を停止し、認証トークンを削除します。

これは、構成が機能しない理由を説明しています。別のユーザー プロバイダーを使用しているにもかかわらず、これを回避する唯一の方法は、フレームワークのクラスInMemoryUserProviderとクラスをコピーし、コピーされたクラスに対してチェックするようにメソッドをUser変更することです。これにより、フロントエンドとバックエンドのユーザー プロバイダーが異なるユーザー クラスを使用し、衝突が発生しなくなります。refreshUser()User

于 2014-11-27T04:10:40.173 に答える