2

私は、従来のユーザー フォームとまったく同じように機能するカスタム プロバイダーに取り組んでいますが、ユーザーを識別するための 2 番目のパラメーター、websiteId を指定する必要があります (動的な Web サイト プレートフォームを作成しています)。

したがって、ユーザー名はもはや一意ではありませんが、ユーザー名と websiteId の組み合わせは一意です。

カスタム認証を正常に作成しました。最後の問題は、リスナーのおかげでドメインから websiteId を取得することです。これは機能しますが、残念ながら、ドメインから Web サイト ID を取得するメソッドは認証プロバイダーの後にロードされるため、時間内にwebsiteIdを取得できません:(

リスナーの優先順位を変更しようとしました (テスト 9999、1024、255、および 0、および負の数 -9999、-1024、-255 など.​​..)、無駄に、常に後で読み込まれます。

ここに私のコード:

services.yml:

services:

    # Listeners _________________

    website_listener:
        class: Sybio\Bundle\WebsiteBundle\Services\Listener\WebsiteListener
        arguments: 
            - @doctrine
            - @sybio.website_manager
            - @translator
            - %sybio.states%
        tags:
            - { name: kernel.event_listener, event: kernel.request, method: onDomainParse, priority: 255 }

    # Security _________________

    sybio_website.user_provider:
        class: Sybio\Bundle\WebsiteBundle\Security\Authentication\Provider\WebsiteUserProvider
        arguments: [@website_listener, @doctrine.orm.entity_manager]

私のリスナーは「website_listener」で、それを sybio_website.user_provider の引数として使用していることがわかります。

ウェブサイト リスナー:

// ...

class WebsiteListener extends Controller
{
    protected $doctrine;

    protected $websiteManager;

    protected $translator;

    protected $websiteId;

    /**
     * @var array
     */
    protected $entityStates;

    public function __construct($doctrine, $websiteManager, $translator, $entityStates)
    {
        $this->doctrine = $doctrine;
        $this->websiteManager = $websiteManager;
        $this->translator = $translator;
        $this->entityStates = $entityStates;
    }

    /**
     * @param Event $event
     */
    public function onDomainParse(Event $event)
    {
        $request = $event->getRequest();

        $website = $this->websiteManager->findOne(array(
            'domain' => $request->getHost(),
            'state' => $this->entityStates['website']['activated'],
        ));

        if (!$website) {
            throw $this->createNotFoundException($this->translator->trans('page.not.found'));
        }

        $this->websiteId = $website->getId();
    }

    /**
     * @param integer $websiteId
     */
    public function getWebsiteId()
    {
        return $this->websiteId;
    }
}

$websiteId はハイドレートされていますが、プロバイダーで表示されるように間に合いません...

ウェブサイト ユーザー プロバイダ:

<?php
namespace Sybio\Bundle\WebsiteBundle\Security\Authentication\Provider;

// ...

class WebsiteUserProvider implements UserProviderInterface
{
    private $em;
    private $websiteId;
    private $userEntity;

    public function __construct($websiteListener, EntityManager $em)
    {
        $this->em = $em;
        $this->websiteId = $websiteListener->getWebsiteId(); // Try to get the website id from my listener, but it's method onDomainParse is not called in time
        $this->userEntity = 'Sybio\Bundle\CoreBundle\Entity\User';
    }

    public function loadUserByUsername($username)
    {
        // I need the websiteId here to identify the user by its username and the website:
        if ($user = $this->findUserBy(array('username' => $username, 'website' => $this->websiteId))) {
            return $user;
        }

        throw new UsernameNotFoundException(sprintf('No record found for user %s', $username));
    }

    // ...
}

したがって、どんなアイデアでも感謝します;)認証構成のセットアップに多くの時間を費やしましたが、websiteIdを時間内に取得できません。残念です:(

あなたの答えをありがとう!

編集:

認証システムの他のファイルも理解する必要がありました.security.yml構成で認識されているため、ロード時にプロバイダーの位置を制御できないと思います:

ウェブサイト認証プロバイダー:

// ...

class WebsiteAuthenticationProvider extends UserAuthenticationProvider
{
    private $encoderFactory;
    private $userProvider;

    /**
     * @param \Symfony\Component\Security\Core\User\UserProviderInterface $userProvider
     * @param UserCheckerInterface $userChecker
     * @param $providerKey
     * @param EncoderFactoryInterface $encoderFactory
     * @param bool $hideUserNotFoundExceptions
     */
    public function __construct(UserProviderInterface $userProvider, UserCheckerInterface $userChecker, $providerKey, EncoderFactoryInterface $encoderFactory, $hideUserNotFoundExceptions = true)
    {
        parent::__construct($userChecker, $providerKey, $hideUserNotFoundExceptions);
        $this->encoderFactory   = $encoderFactory;
        $this->userProvider     = $userProvider;
    }

    /**
     * {@inheritdoc}
     */
    protected function retrieveUser($username, UsernamePasswordToken $token)
    {
        $user = $token->getUser();
        if ($user instanceof UserInterface) {
            return $user;
        }

        try {
            $user = $this->userProvider->loadUserByUsername($username);

            if (!$user instanceof UserInterface) {
                throw new AuthenticationServiceException('The user provider must return a UserInterface object.');
            }

            return $user;
        } catch (UsernameNotFoundException $notFound) {
            throw $notFound;
        } catch (\Exception $repositoryProblem) {
            throw new AuthenticationServiceException($repositoryProblem->getMessage(), $token, 0, $repositoryProblem);
        }
    }

    // ...
}

工場:

// ...

class WebsiteFactory extends FormLoginFactory
{
    public function getKey()
    {
        return 'website_form_login';
    }

    protected function getListenerId()
    {
        return 'security.authentication.listener.form';
    }

    protected function createAuthProvider(ContainerBuilder $container, $id, $config, $userProviderId)
    {
        $provider = 'security.authentication_provider.sybio_website.'.$id;
        $container
            ->setDefinition($provider, new DefinitionDecorator('security.authentication_provider.sybio_website'))
            ->replaceArgument(0, new Reference($userProviderId))
            ->replaceArgument(2, $id)
        ;

        return $provider;
    }
}

SybioWebsiteBundle (依存関係):

// ...

class SybioWebsiteBundle extends Bundle
{
    public function build(ContainerBuilder $container)
    {
        parent::build($container);

        $extension = $container->getExtension('security');
        $extension->addSecurityListenerFactory(new WebsiteFactory());
    }
}

安全:

security:
    firewalls:
        main:
            provider: website_provider
            pattern:    ^/
            anonymous: ~
            website_form_login:
                login_path:  /login.html
                check_path:  /login
            logout:
                path:   /logout.html
                target: /

    providers:
        website_provider:
            id: sybio_website.user_provider
4

3 に答える 3

3

Firewall::onKernelRequest は優先度 8 (sf2.2) で登録されます。優先度 9 は、リスナーが最初に呼び出されることを保証する必要があります (私にとってはうまくいきます)。

単一の sf2.2 アプリ内にサブドメイン固有の「キャンペーン」サイトを作成するという同様の問題がありました: {campaign}.{domain} 。すべてのユーザーには多くのキャンペーンがあり、私もあなたと同じように、特定のキャンペーンを持たないユーザーがログインできないようにしたいと考えていました。

私の解決策は、Doctrine フィルターを作成して、{campaign}.{domain} の下で作成されたすべての関連クエリにキャンペーン基準を追加することでした。kernel.request リスナー (優先度 9!) は、汎用ユーザー プロバイダーが UserByUsername をロードしようとする前に、フィルターを有効にする役割を果たします。私はmongodbを使用していますが、考え方はORMでも同様です。

最良の部分は、私がまだストック認証クラスを使用していることです。これは基本的にそれにあるすべてです:

config.yml:

doctrine_mongodb:
    document_managers:
        default:
            filters:
                campaign:
                    class: My\Filter\CampaignFilter
                    enabled: false

CampaignFilter.php:

class CampaignFilter extends BsonFilter
{
    public function addFilterCriteria(ClassMetadata $targetMetadata)
    {
        $class = $targetMetadata->name;
        $campaign = $this->parameters['campaign'];
        $campaign = $campaign instanceof Campaign ? $campaign->getId() : $campaign;
        if ($targetMetadata->hasField('campaign')) {
            return array('campaign' => $this->parameters['campaign']);
        }
        if ($targetMetadata->hasField('campaigns')) {
            return array('campaigns' => $this->parameters['campaign']);
        }
        return array();
    }
}

私のリスナーは次のように宣言されています。

    <service id="my.campaign_listener" class="My\EventListener\CampaignListener">
        <tag name="kernel.event_listener" event="kernel.request" method="onKernelRequest" priority="9" />
        <argument type="service" id="doctrine.odm.mongodb.document_manager" />
    </service>

リスナー クラス:

class CampaignListener
{
    private $dm;

    public function __construct(DocumentManager $dm)
    {
        $this->dm = $dm;
    }

    public function onKernelRequest(GetResponseEvent $event)
    {
        if (HttpKernelInterface::MASTER_REQUEST != $event->getRequestType()) {
            return;
        }

        $request = $event->getRequest();
        if ($campaign = $request->attributes->get('campaign', false)) {
            $filters = $this->dm->getFilterCollection();
            $filter = $filters->enable('campaign');
            $filter->setParameter('campaign', $campaign);
        }
    }
}

「キャンペーン」は、ルーティング構成のおかげで、ここのリクエストで利用できます。

campaign:
    resource: "@My/Controller/CampaignController.php"
    type:     annotation
    host: "{campaign}.{domain}"
    defaults:
        campaign: test
        domain: %domain%
    requirements:
        domain: %domain%

.. %domain% は config.yml または config_dev.yml のパラメータです

于 2013-03-14T00:23:29.150 に答える
0

SecurityExtension.php でわかるように、使用されているファクトリには優先度システムはありません。ファクトリを配列の最後に追加するだけです。それだけです。したがって、カスタム認証を symfony のセキュリティ コンポーネントの前に置くことは不可能です。

オプションは、 DaoAuthenticationProvider クラス パラメータをクラスでオーバーライドすることです。symfony2 がファクトリから、タグと優先度を使用してカスタム認証を追加できるレジストリに変更されることを願っています。

于 2015-08-24T08:54:44.163 に答える
0

benki07 によって提供される応答のように、これは優先順位の問題です。リスナーを Firewall::onKernelRequest の前に配置する必要があります。

次に、リスナーが呼び出されます->ファイアウォールが呼び出され、認証リスナーが登録されたwebSiteIdで呼び出されます。

于 2013-02-18T10:14:54.020 に答える