もう一度Symfony2のユーザーリソース特権アプローチのセキュリティについて

少し前にSymfony2を取り上げました。 それ以前は、Zend1とのコミュニケーションにおいてかなり豊富な経験があったという事実にもかかわらず、私にとっては参入障壁が高いことが判明しました。 十分に読んで、何かが私のためにうまくいき始めました。 最大の問題は、アクセス権の制限の問題が原因でした。 ほとんどすべての検索で、フレームワークの標準配信からセキュリティモジュールの機能を拡張する方法に関するFOSUserBundleまたはスニペットの情報に移動しました。 かさばるFOSUserBundleには、自分には利点がありませんでした。 したがって、この記事では、Symfony2セキュリティを自分のニーズに合わせてどのように仕上げたかについて説明します。 目標は次のとおりでした:symfony2 +セキュリティ+ユーザーの役割に応じたオブジェクトレベルでのアクセス権の区別。 この記事では、ロールと累積特権の継承については説明しません。これらの情報は簡単に見つけることができます。 私のプロジェクトの権利スキーム:許可されていないものはすべて禁止されています。 1人のユーザーには厳密に1つの役割があります。 ロールは、異なる特権セットを持つさまざまなリソースにアクセスできます。 異なるロールは、異なるまたは同等の特権セットで同じリソースにアクセスできます。 コードをできる限り抽象化しようとはしませんが、サービス機器の注文注文の機能に関連するプロジェクトの断片を使用します。



要するに。 プロジェクトが正しく構成され、その中にBackendWorkorderBundleが作成され、すべてのルーターとファイアウォールが構成されています。 つまり アクセス権以外のすべてがあります。 認証を含む。 データベース設計には、MySQL Workbenchツールが使用されました。 素晴らしいもの。 Linux用のバージョンがあります。 テーブル構造は次のようになります。



-- ----------------------------------------------------- -- Table `backend_role` -- ----------------------------------------------------- CREATE TABLE IF NOT EXISTS `backend_role` ( `role_id` INT NOT NULL AUTO_INCREMENT, `name` VARCHAR(45) NULL, `description` VARCHAR(45) NULL, PRIMARY KEY (`role_id`)) ENGINE = InnoDB; -- ----------------------------------------------------- -- Table `backend_user` -- ----------------------------------------------------- CREATE TABLE IF NOT EXISTS `backend_user` ( `user_id` INT NOT NULL AUTO_INCREMENT, `role_id` INT NOT NULL, `firstname` VARCHAR(45) NULL, `lastname` VARCHAR(45) NULL, `printname` VARCHAR(45) NULL, `username` VARCHAR(45) NULL, `salt` VARCHAR(255) NULL, `password` VARCHAR(255) NULL, `created` DATETIME NULL, `updated` DATETIME NULL, `last_login` DATETIME NULL, `is_active` TINYINT(1) NULL, PRIMARY KEY (`user_id`), INDEX `fk_backend_user_backend_role1_idx` (`role_id` ASC), CONSTRAINT `fk_backend_user_backend_role1` FOREIGN KEY (`role_id`) REFERENCES `parts`.`backend_role` (`role_id`) ON DELETE NO ACTION ON UPDATE NO ACTION) ENGINE = InnoDB; -- ----------------------------------------------------- -- Table `backend_rule` -- ----------------------------------------------------- CREATE TABLE IF NOT EXISTS `backend_rule` ( `rule_id` INT NOT NULL AUTO_INCREMENT, `role_id` INT NOT NULL, `resource_id` VARCHAR(255) NULL, `privileges` TEXT NULL, PRIMARY KEY (`rule_id`), INDEX `fk_backend_rule_backend_role1_idx` (`role_id` ASC), CONSTRAINT `fk_backend_rule_backend_role1` FOREIGN KEY (`role_id`) REFERENCES `parts`.`backend_role` (`role_id`) ON DELETE NO ACTION ON UPDATE NO ACTION) ENGINE = InnoDB;
      
      





特権を確認する方法は2つあります。

1.小枝is_granted('[ ]', [])



からis_granted('[ ]', [])





2.コントローラーから$this->get('security.context')->isGranted('[ ]', [])





2番目の引数はオプションですが、私のプロジェクトの目的に必要です(投票者コードでもう少し低くなります)。 htmlページからオブジェクトを除外しても、コントローラーでのデータ検証はキャンセルされません。



有権者コード。 プロジェクトには、バックエンド全体の最も一般的な機能を組み込んだ別のBackendCoreBundleバンドルがあることを忘れていました。 有権者の詳細については、 こちらをご覧ください

 <?php // /src/Backend/CoreBundle/Security/Authorization/Voter/PrivilegeVoter.php namespace Backend\CoreBundle\Security\Authorization\Voter; use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\Security\Core\Authorization\Voter\VoterInterface; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; class PrivilegeVoter implements VoterInterface { public function supportsAttribute($attribute) { return true; } public function supportsClass($class) { return in_array($class, array( 'Backend\WorkorderBundle\Entity\Workorder' )); } public function vote(TokenInterface $token, $object, array $attributes) { //  voter    . //           . if ( !($this->supportsClass(get_class($object))) ) { return VoterInterface::ACCESS_ABSTAIN; } foreach ($attributes as $attribute) { //      if ( !$this->supportsAttribute($attribute) ) { return VoterInterface::ACCESS_ABSTAIN; } } //   $user = $token->getUser(); $privileges = $user->getPrivileges(); $resourceId = $object->getResourceId(); $acess_granted = false; foreach ($attributes as $attribute) { if (isset($privileges[$resourceId])) { $resource_privileges = $privileges[$resourceId]; if (in_array($attribute, $resource_privileges)) { $acess_granted = true; } else { $acess_granted = false; break; } } } if ($acess_granted) return VoterInterface::ACCESS_GRANTED; return VoterInterface::ACCESS_DENIED; } }
      
      







ユーザーのgetPrivileges関数は、backend_userテーブルに関連付けられたdoctrineオブジェクトで宣言されます

 <?php ///src/Backend/CoreBundle/Entity/BackendUser.php namespace Backend\CoreBundle\Entity; use Doctrine\ORM\Mapping as ORM; use Symfony\Component\Security\Core\User\AdvancedUserInterface; /** * BackendUser * * @ORM\Table(name="backend_user") * @ORM\Entity */ class BackendUser implements AdvancedUserInterface, \Serializable { .. public function getPrivileges() { //  : backend_user->backend_role->backend_rule // $rule->getPrivileges()    privileges  backend_rule //         resource_id, //         (  ) $rules = $this->getRole()->getRules(); $result = array(); foreach ($rules as $rule){ $result[$rule->getResourceId()] = explode(",", $rule->getPrivileges()); } return $result; } .. }
      
      







/app/config/security.ymlに有権者を登録します



 services: security.access.privilege_voter: class: Backend\CoreBundle\Security\Authorization\Voter\PrivilegeVoter public: false tags: - { name: security.voter }
      
      





おそらく、$ object-> getResourceId()が投票関数で呼び出されることに気づいたでしょう。 メソッドは次のようになります

 <?php // /src/Backend/WorkorderBundle/Entity/Workorder.php namespace Backend\WorkorderBundle\Entity; use Doctrine\Common\Collections\ArrayCollection; use Doctrine\ORM\Mapping as ORM; /** * Workorder * * @ORM\Table(name="workorder") * @ORM\Entity */ class Workorder { .. public function getResourceId() { //           //   Backend\WorkorderBundle\Entity\Workorder return \Doctrine\Common\Util\ClassUtils::getClass($this); } .. }
      
      







それだけです! 誰かがこのアプローチの短所とスケーリングの問題の可能性を指摘できるなら、いつものように批判を歓迎します-私はとても幸せです。



All Articles