21

まず、私は 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;
}

(refreshUser似てる)

リクエストごとにSymfonyにユーザーロールを更新させる方法はありますか?

4

8 に答える 8

22

そのため、実行可能な解決策を見つけようとして数日間試行し、Symfony2 ユーザーのメーリング リストに貢献した後、ついに見つけました。以下は、 https://groups.google.com/d/topic/symfony2/NDBb4JN3mNc/discussionでの議論から派生したものです。

Symfony\Component\Security\Core\User\EquatableInterfaceオブジェクトの同一性を比較するためのものではなく、正確に比較するためのインターフェースがあることが判明しました

セキュリティと再認証のコンテキストで 2 つのオブジェクトが等しいかどうかをテストする

そのインターフェイスをユーザー クラス (既に実装しているものUserInterface) に実装します。唯一必要なメソッドを実装してisEqualTo(UserInterface $user)、現在のユーザーのロールが渡されたユーザーのロールと異なる場合に false を返すようにします。

注: User オブジェクトはセッションでシリアル化されます。シリアライゼーションの仕組みにより、ユーザー オブジェクトのフィールドにロールを格納し、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注釈がない、ファイアウォールで必要な役割がないなど)、更新は実際には行われず、ユーザーは「認証されていない」状態のままになります。

何らかの種類の承認チェックを実行するページにアクセスするとすぐに、ユーザーの役割が更新され、プロファイラーのツールバーに緑色のドットと「認証済み: はい」が表示されたユーザーが再び表示されます。

それは私にとって許容できる動作です-それが役に立てば幸いです:)

于 2012-12-12T09:54:47.367 に答える
12

security.yml (または代替) で:

security:
    always_authenticate_before_granting: true

私の人生で最も簡単なゲーム。

于 2015-02-28T12:44:14.267 に答える
3

独自の EntityUserProvider を実装し、 loadByUsername($username) メソッドをオーバーライドすることで、この動作を実現します。

   /**
    * Load an user from its username
    * @param string $username
    * @return UserInterface
    */
   public function loadUserByUsername($username)
   {
      $user = $this->repository->findOneByEmailJoinedToCustomerAccount($username);

      if (null === $user)
      {
         throw new UsernameNotFoundException(sprintf('User "%s" not found.', $username));
      }

      //Custom function to definassigned roles to an user
      $roles = $this->loadRolesForUser($user);

      //Set roles to the user entity
      $user->setRoles($roles);

      return $user;
   }

setRoles電話をかけるたびに電話をかけるのがコツですloadByUsername...お役に立てば幸いです

于 2012-12-10T11:07:26.623 に答える
1

解決策は、Doctrine のpostUpdateイベントでサブスクライバーをハングアップすることです。更新されたエンティティがユーザーであり、ログに記録されたのと同じユーザーである場合、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);
            }
        }
    }
}
于 2013-05-01T07:12:20.217 に答える
0

申し訳ありませんが、コメントで返信できないため、質問にリプレイします。symfony セキュリティの初心者がカスタム パスワード認証でロール更新作業を取得しようとすると、関数 authenticateToken 内で:

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

また、DB/LDAP またはどこからでもパスワードをチェックしないでください。ユーザーがシステムに入った場合、$token はユーザー名だけで役割はありません。

于 2015-07-10T15:22:01.690 に答える