web-dev-qa-db-ja.com

ユーザーロールはいつ更新され、どのように強制するのですか?

まず、FOSUserBundleを使用していませんが、独自のモデルレイヤー(ここではDoctrine/Mongoなどはありません)やその他の非常にカスタムな動作を持つレガシーシステムを移植しているため、使用できません。

コントローラーとビューでネイティブのsymfonyセキュリティを使用できるように、レガシーロールシステムをSymfonyに接続しようとしています。

私の最初の試みは、_Symfony\Component\Security\Core\User\UserInterface_からgetRoles()メソッド内のすべてのユーザーのロールをロードして返すことでした。最初は、それがうまくいったように見えました。しかし、詳しく調べてみると、これらの役割はユーザーがログインしたときにのみ更新されることがわかりました。つまり、ユーザーに役割を付与または取り消すと、変更を有効にするためにユーザーはログアウトしてから再度ログインする必要があります。ただし、ユーザーからセキュリティロールを取り消す場合は、すぐに適用したいので、その動作は受け入れられません。

私が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にユーザーロールを更新させる方法はありますか?

21
netmikey

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

オブジェクトIDを比較することを目的としていないが、正確に次のインターフェイス_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_注釈がない、ファイアウォールで必要な役割がないなど)、更新は実際には行われず、ユーザーは「認証されていない」状態に保たれます。

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

それは私にとって許容できる振る舞いです-それが役に立ったことを願っています:)

21
netmikey

Security.yml(または代替):

security:
    always_authenticate_before_granting: true

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

11
Marc

コントローラから、ユーザーにロールを追加し、データベースに保存した後、次のコマンドを呼び出すだけです。

// Force refresh of user roles
$token = $this->get('security.context')->getToken()->setAuthenticated(false);
7
Simon Epskamp

見てください ここ 、set always_authenticate_before_grantingからtrue at security.yml

4
psylosss

この動作は、独自の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を呼び出すことです...それが役立つことを願っています

3
AlterPHP

解決策は、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);
            }
        }
    }
}
1
Petr Sedláček

今Symfony4.1はしばらくの間ここにあります、
そして私は ユーザーを手動で認証する によって問題を解決します。

0
BBeta

申し訳ありませんが、コメントで返信できないので、質問にリプレイします。 symfonyセキュリティの初心者がカスタムパスワード認証でロールリフレッシュを機能させようとした場合、関数authenticateToken内で:

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

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

0
Agris