3

私は Symfony 2 を使用して Web アプリケーションに取り組んでいます。より具体的には、Symfony Cookbook の指示に従って、カスタム認証プロバイダーを作成しようとしています。

ログインフォームに入力されたユーザー名とパスワードをデータベースに保存されているものと照合してはならないため、カスタム認証プロバイダーを作成する必要があります。しかし、とにかく、それは私の問題ではありません。

問題は、私のコードを読んで(以下に貼り付けました)、パスワードが正しいかどうかに関係なく(データベースに保存されているものと比較して)、誰でもデータベースに既にあるユーザー名を入力してログインできるはずです。入力したユーザー名がデータベースに存在する場合。しかし、そうではありません。魔法のように、パスワードがチェックされ、間違ったパスワードでログインしようとすると「資格情報が正しくありません」というエラー メッセージが表示されます。

コードを貼り付けます。私の app/config/security.yml :

jms_security_extra:
    secure_all_services: false
    expressions: true

security:
    encoders:
        Symfony\Component\Security\Core\User\User: plaintext
        Epilille\UserBundle\Entity\User: plaintext

    role_hierarchy:
        ROLE_STUDENT:     ROLE_USER
        ROLE_AER:         ROLE_STUDENT
        ROLE_PROF:        ROLE_AER
        ROLE_ADMIN:       ROLE_PROF
        ROLE_SUPER_ADMIN: [ROLE_ADMIN, ROLE_ALLOWED_TO_SWITCH]

    providers:
        in_memory:
            memory:
                users:
                    user:  { password: userpass, roles: [ 'ROLE_USER' ] }
                    admin: { password: adminpass, roles: [ 'ROLE_ADMIN' ] }
                    student:  { password: studentpass, roles: ['ROLE_STUDENT'] }
                    mestag_a: { password: mestag_apass, roles: ['ROLE_AER'] }
        intra_provider:
            entity: { class: EpililleUserBundle:User, property: username }

    firewalls:
        dev:
            pattern:  ^/(_(profiler|wdt)|css|images|js)/
            security: false

        login:
            pattern:  ^/login$
            anonymous: true

        intra_secured:
            pattern:  ^/
            intra: true
            provider: intra_provider
            form_login:
              login_path: login
              check_path: login_check
              default_target_path: epilille_home_homepage
            logout:
                path:   logout
                target: login

    access_control:
        # - { path: ^/login$, roles: IS_AUTHENTICATED_ANONYMOUSLY}

私の UserBundle のアーキテクチャ:

.
├── Controller/
│   └── SecurityController.php
├── DataFixtures/
│   └── ORM/
│       └── Users.php
├── DependencyInjection/
│   ├── Configuration.php
│   ├── EpililleUserExtension.php
│   └── Security/
│       └── Factory/
│           └── IntraFactory.php
├── Entity/
│   ├── User.php
│   └── UserRepository.php
├── EpililleUserBundle.php
├── EpiLogin.class.php
├── Resources/
│   ├── config/
│   │   └── services.yml
│   ├── doc/
│   │   └── index.rst
│   ├── public/
│   │   ├── css/
│   │   ├── images/
│   │   └── js/
│   ├── translations/
│   │   └── messages.fr.xlf
│   └── views/
│       ├── layout.html.twig
│       └── User/
│           └── login.html.twig
└── Security/
    ├── Authentication/
    │   ├── Provider/
    │   │   └── IntraProvider.php
    │   └── Token/
    │       └── IntraUserToken.php
    ├── Firewall/
    │   └── IntraListener.php
    └── User/
        └── IntraUserProvider.php

私の工場:

<?php

namespace       Epilille\UserBundle\DependencyInjection\Security\Factory;

use         Symfony\Component\DependencyInjection\ContainerBuilder;
use         Symfony\Component\DependencyInjection\Reference;
use         Symfony\Component\DependencyInjection\DefinitionDecorator;
use         Symfony\Component\Config\Definition\Builder\NodeDefinition;
use         Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\SecurityFactoryInterface;

class           IntraFactory implements SecurityFactoryInterface
{
  public function   create(ContainerBuilder $container, $id, $config, $userProvider, $defaultEntryPoint)
  {
    $providerId = 'security.authentication.provider.intra.'.$id;
    $container
      ->setDefinition($providerId, new DefinitionDecorator('intra.security.authentication.provider'))
      ->replaceArgument(0, new Reference($userProvider))
      ;

    $listenerId = 'security.authentication.listener.intra.'.$id;
    $listener = $container->setDefinition($listenerId, new DefinitionDecorator('intra.security.authentication.listener'));

    return array($providerId, $listenerId, $defaultEntryPoint);
  }

  public function   getPosition()
  {
    return 'pre_auth';
  }

  public function   getKey()
  {
    return 'intra';
  }

  public function   addConfiguration(NodeDefinition $node)
  {
  }
}

私のトークン:

<?php

namespace       Epilille\UserBundle\Security\Authentication\Token;

use         Symfony\Component\Security\Core\Authentication\Token\AbstractToken;

class           IntraUserToken extends AbstractToken
{
  public function   __construct(array $roles = array())
  {
    parent::__construct($roles);
    $this->setAuthenticated(count($roles) > 0);
  }

  public function   getCredentials()
  {
    return '';
  }
}

私の認証プロバイダー:

<?php

namespace       Epilille\UserBundle\Security\Authentication\Provider;

use         Symfony\Component\Security\Core\Authentication\Provider\AuthenticationProviderInterface;
use         Symfony\Component\Security\Core\User\UserProviderInterface;
use         Symfony\Component\Security\Core\Exception\AuthenticationException;
use         Symfony\Component\Security\Core\Exception\NonceExpiredException;
use         Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use         Epilille\UserBundle\Security\Authentication\Token\IntraUserToken;

class           IntraProvider implements AuthenticationProviderInterface
{
  private       $userProvider;

  public function   __construct(UserProviderInterface $userProvider)
  {
    $this->userProvider = $userProvider;
  }

  public function   authenticate(TokenInterface $token)
  {
    $user = $this->userProvider->loadUserByUsername($token->getUsername());

    if ($user) {
      $authenticatedToken = new IntraUserToken($user->getRoles());
      $authenticatedToken->setUser($user);

      return $authenticatedToken;
    }

    throw new AuthenticationException('The Intranet authentication failed.');
  }

  public function   supports(TokenInterface $token)
  {
    // I noticed something with this method I tell you below
    return $token instanceof IntraUserToken;
  }
}

私のリスナー:

<?php

namespace       Epilille\UserBundle\Security\Firewall;

use         Symfony\Component\HttpFoundation\Response;
use         Symfony\Component\HttpKernel\Event\GetResponseEvent;
use         Symfony\Component\Security\Http\Firewall\ListenerInterface;
use         Symfony\Component\Security\Core\Exception\AuthenticationException;
use         Symfony\Component\Security\Core\SecurityContextInterface;
use         Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface;
use         Epilille\UserBundle\Security\Authentication\Token\IntraUserToken;
use         Epilille\UserBundle\Security\Authentication\Provider\IntraProvider;

class           IntraListener implements ListenerInterface
{
  protected     $securityContext;
  protected     $authenticationManager;

  public function   __construct(SecurityContextInterface $securityContext, AuthenticationManagerInterface $authenticationManager)
  {
    $this->securityContext = $securityContext;
    $this->authenticationManager = $authenticationManager;
  }

  public function   handle(GetResponseEvent $event)
  {
    // I have to do this check because it seems like this function is called several times and if
    // it's not the first time, it will try to reset a new IntraUserToken with _username and _password not set.
    if ($this->securityContext->getToken()) {
      return;
    }

    $request = $event->getRequest();

    $token = new IntraUserToken();
    $token->setUser($request->get('_username'));
    $token->password = $request->get('_password');

    try {
      $authToken = $this->authenticationManager->authenticate($token);

      $this->securityContext->setToken($authToken);
    } catch (AuthenticationException $failed) {
      // ... you might log something here

      // To deny the authentication clear the token. This will redirect to the login page.
      $this->securityContext->setToken(null);
      return;

      // Deny authentication with a '403 Forbidden' HTTP response
      /* $response = new Response(); */
      /* $response->setStatusCode(403); */
      /* $event->setResponse($response); */
    }
  }
}

したがって、このコードでは、次のようになります。

  • データベースに存在しないユーザー名を入力すると、「Bad credentials」というエラー メッセージが表示されます。
  • データベースに存在するユーザー名を入力した場合:
    • 間違ったパスワードを使用すると、「資格情報が正しくありません」というメッセージが表示されます。
    • 正しいパスワードを使用すると、認証され、プロファイラーは、トークン クラス 'UsernamePasswordToken' でログに記録されていると言います。

ここで、メソッド IntraProvider::supports について説明したいと思います。今、それはそのようなものです:

public function       supports(TokenInterface $token)
{
  return $token instanceof IntraUserToken;
}

そして、そのように変更すると:

public function       supports(TokenInterface $token)
{
  return (true);
}

適切なユーザー名を入力した場合とは違いがあります (パスワードは関係ありません) : 私は認証されており、プロファイラーは私がトークン クラス 'IntraUserToken' を使用していると言っています (それはまさに私が望んでいるもののようです)。 .

だから私の問題は次のとおりです。

  • 認証ソリューションが IntraProvider::supports メソッドの最初のバージョンで機能しないのはなぜですか?
  • IntraProvider::supports メソッドで毎回 true を返すと機能するのはなぜですか?

クックブックでは、このメソッドの最初のバージョンで行ったのと同じように行っているので、毎回 true を返すのは得策ではないと思いますね。

お時間とご回答ありがとうございます。

4

0 に答える 0