Symfony 2中自定义身份验证提供程序出现“错误凭据”错误

时间:2013-06-26 18:54:15

标签: php authentication symfony forms-authentication symfony-2.2

我正在使用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 ::支持的方法。现在,就像那样:

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

如果我改变它:

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

我输入一个好的用户名(不管密码是什么)的情况有所不同:我已经过身份验证,而且探查器现在说我正在使用Token类'IntraUserToken'(它似乎正是我想要。)

所以我的问题是:

  • 为什么我的身份验证解决方案不适用于第一版IntraProvider :: supports方法?
  • 为什么每次在IntraProvider :: supports方法中返回true都会有效?

在Cookbook中,他们就像我在这个方法的第一个版本中那样做,所以我不认为每次都返回true是个好主意,是吗?

我希望自己明确表达自己的问题,感谢您的时间和答案!

0 个答案:

没有答案