具有两种身份验证方法的 FOSUserBundle

FOSUserBundle with two authentication methods

我有一个 Symfony 2.8 项目,我正在其中使用 FOSUserBundleFOSUser 身份验证方法使用 fos_user table 来识别和验证凭证以及使用 sha512 加密的密钥。

是否可以修改或扩展某些 classes,以便万一在 table fos_user 中找不到用户,请在用户 [=] 中查找68=] 其中密钥使用 md5?

加密

根据madshvero的日落更新:

我创建了一个用户class:

namespace AppBundle\Security\User;

use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Core\User\EquatableInterface;

class WebserviceUser implements UserInterface, EquatableInterface
{
    private $username;
    private $password;
    private $salt;
    private $roles;

    public function __construct($username, $password, $salt, array $roles)
    {
        $this->username = $username;
        $this->password = $password;
        $this->salt = $salt;
        $this->roles = $roles;
    }

    public function getRoles()
    {
        return $this->roles;
    }

    public function getPassword()
    {
        return $this->password;
    }

    public function getSalt()
    {
        return $this->salt;
    }

    public function getUsername()
    {
        return $this->username;
    }

    public function eraseCredentials()
    {
    }

    public function isEqualTo(UserInterface $user)
    {
        if (!$user instanceof WebserviceUser) {
            return false;
        }

        if ($this->password !== $user->getPassword()) {
            return false;
        }

        if ($this->salt !== $user->getSalt()) {
            return false;
        }

        if ($this->username !== $user->getUsername()) {
            return false;
        }

        return true;
    }
}

我还创建了用户提供程序:

namespace AppBundle\Security\User;

use AppBundle\Security\User\WebserviceUser;
use Symfony\Component\Security\Core\User\UserProviderInterface;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Core\Exception\UsernameNotFoundException;
use Symfony\Component\Security\Core\Exception\UnsupportedUserException;

class WebserviceUserProvider implements UserProviderInterface
{
    public function loadUserByUsername($username)
    {
        // make a call to your webservice here
        $userData = true;
        // pretend it returns an array on success, false if there is no user

        if ($userData) {
            $username = 'prueba';
            $password = 'e10adc3949ba59abbe56e057f20f883e'; // md5('123456')
            $salt = '';`enter code here`
            $roles = [ROLE_SUPER_ADMIN];
            // ...

            return new WebserviceUser($username, $password, $salt, $roles);
        }

        throw new UsernameNotFoundException(
            sprintf('Username "%s" does not exist.', $username)
        );
    }

    public function refreshUser(UserInterface $user)
    {
        if (!$user instanceof WebserviceUser) {
            throw new UnsupportedUserException(
                sprintf('Instances of "%s" are not supported.', get_class($user))
            );
        }

        return $this->loadUserByUsername($user->getUsername());
    }

    public function supportsClass($class)
    {
        return WebserviceUser::class === $class;
    }
}

并修改security.yml:

security:
    access_denied_url:  /login
    encoders:
        FOS\UserBundle\Model\UserInterface: sha512
        AppBundle\Security\User\WebserviceUser: md5

    providers:
        chain_provider:
            chain:
                providers: [fos_userbundle, webservice]

        fos_userbundle:
            id: fos_user.user_provider.username

        webservice:
            id: app.webservice_user_provider

    firewalls:
        main:
            pattern:    ^/
            fr3d_ldap:  ~
            form_login:
                provider:   fos_userbundle
                check_path: /login_check
                login_path: /login
                always_use_default_target_path: true
                default_target_path: /
            logout:
                path: /logout
                target: /login
            anonymous:    true
        dev:
            pattern: ^/(_(profiler|wdt)|css|images|js)/
            security: false
        login:
            pattern: ^/login$
            security: false

当然我也修改了services.yml增加了服务app.webservice_user_provider: 服务: app.form.group: class: AppBundle\Form\GroupFormType 标签: - { 名称:form.type,别名:app_group_registration }

    app.form.user:
        class: AppBundle\Form\ProfileFormType
        tags:
            - { name: form.type, alias: app_user_profile }

    app.webservice_user_provider:
        class: AppBundle\Security\User\WebserviceUserProvider

完成后,行为是系统允许 fos_user 提供商的用户访问,但不允许我的自定义提供商的用户访问。什么失败了?

这是日志:

[2017-02-16 11:37:08] request.INFO: 匹配的路线 "fos_user_security_check"。 {"route_parameters":{"_controller":"AppBundle\Controller\SecurityController::checkAction","_route":"fos_user_security_check"},"request_uri":"http://127.0.0.1:8000/app_dev.php/login_check"} []

[2017-02-16 11:37:08] doctrine.DEBUG: SELECT t0.id_aspirante AS id_aspirante1, t0.correo AS correo2, t0.clave AS clave3, t0.status_cuenta AS status_cuenta4 FROM aspirantes2 t0 WHERE t0.correo = ? LIMIT 1 ["userFoo"] []

[2017-02-16 11:37:08] doctrine.DEBUG: SELECT t0.id_aspirante AS id_aspirante1, t0.correo AS correo2, t0.clave AS clave3, t0.status_cuenta AS status_cuenta4 FROM aspirantes2 t0 WHERE t0.correo = ? LIMIT 1 ["userFoo"] []

[2017-02-16 11:37:08] security.INFO: Authentication request failed. {"exception":"[object] (Symfony\Component\Security\Core\Exception\AuthenticationServiceException(code: 0): The user provider must return a UserInterface object. at /home/userx/projects/myproj/vendor/symfony/symfony/src/Symfony/Component/Security/Core/Authentication/Provider/DaoAuthenticationProvider.php:94, Symfony\Component\Security\Core\Exception\AuthenticationServiceException(code: 0): The user provider must return a UserInterface object. at /home/userx/projects/myproj/vendor/symfony/symfony/src/Symfony/Component/Security/Core/Authentication/Provider/DaoAuthenticationProvider.php:86)"} []

[2017-02-16 11:37:08] security.DEBUG: Authentication failure, redirect triggered. {"failure_path":"/login"} []

[2017-02-16 11:37:08] request.INFO: Matched route "fos_user_security_login". {"route_parameters":{"_controller":"AppBundle\Controller\SecurityController::loginAction","_route":"fos_user_security_login"},"request_uri":"http://127.0.0.1:8000/app_dev.php/login"} []

[2017-02-16 11:37:08] security.INFO: Populated the TokenStorage with an anonymous Token. [] []

[2017-02-16 11:37:08] request.INFO: Matched route "_wdt". {"route_parameters":{"_controller":"web_profiler.controller.profiler:toolbarAction","token":"c368df","_route":"_wdt"},"request_uri":"http://127.0.0.1:8000/app_dev.php/_wdt/c368df"} []

[2017-02-16 11:37:08] security.INFO: Populated the TokenStorage with an anonymous Token. [] []

[2017-02-16 11:37:08] security.DEBUG: Access denied, the user is not fully authenticated; redirecting to authentication entry point. {"exception":"[object] (Symfony\Component\Security\Core\Exception\AccessDeniedException(code: 403): Access Denied. at /home/userx/projects/myproj/vendor/symfony/symfony/src/Symfony/Component/Security/Http/Firewall/AccessListener.php:70)"} []

[2017-02-16 11:37:08] security.DEBUG: Calling Authentication entry point. [] []

[2017-02-16 11:37:08] request.INFO: Matched route "fos_user_security_login". {"route_parameters":{"_controller":"AppBundle\Controller\SecurityController::loginAction","_route":"fos_user_security_login"},"request_uri":"http://127.0.0.1:8000/app_dev.php/login"} []

[2017-02-16 11:37:08] security.INFO: Populated the TokenStorage with an anonymous Token. [] []

您可以通过创建一个 custom provider 来检查两个表中的用户,并 returns 它找到的用户。

然后您可以更新 app/config/security.yml 以使用您的提供商而不是 FOSUserBundle 提供的提供商:

security:
    providers:
        fos_userbundle:
            id: the.id.of.your.provider

阅读文档后,我能够理解身份验证方法的逻辑,并且发现使用来自数据库的身份验证提供程序对我的项目更加方便和容易。真的是一个非常简单的解决方案,记录在:How to Load Security Users from the Database (the Entity Provider)

在我使用 FOSUserBundle 的情况下,有两个考虑因素:

  1. 它们必须存在两种身份验证方法:一种由 FOSUserBundle 提供,一种由 MyBundle 提供。
  2. 身份验证过程应尝试以两种方法对用户进行身份验证。

为此,除了 How to Load Security Users from the Database (the Entity Provider) 中给出的建议外,您还必须修改 security.yml 的某些部分,使其看起来像这样::

encoders:
    // The database method of FOSUserBundle
    FOS\UserBundle\Model\UserInterface:
        algorithm: sha512
    // The data base method of mine
    MyBundle\Entity\MyEntity: 
        //This values depends on how the keys were encrypted in the database
        algorithm: md5
        encode_as_base64: false
        iterations: 0

providers:
    chain_provider:
        chain:
            providers: [fos_userbundle, aspirante_db]

    fos_userbundle:
        id: fos_user.user_provider.username

    myentity_db:
        entity: { class: MyBundle\Entity\MyEntity, property: username }

firewalls:
    main:                                                                                                                              
        pattern:    ^/
        fr3d_ldap:  ~
        form_login:
            provider:   chain_provider //This is the important change
            check_path: /login_check
            login_path: /login
            always_use_default_target_path: true
            default_target_path: /
        logout:
            path: /logout
            target: /login
        anonymous:    true
    dev:
        pattern: ^/(_(profiler|wdt)|css|images|js)/
        security: false
    login:
        pattern: ^/login$
        security: false

就是这样。我希望将来有人可以利用这个post。非常感谢 madshvero 的指导。