FOSUserBundle с двумя методами аутентификации


У меня есть проект Symfony 2.8, в котором я использую FOSUserBundle. Метод аутентификации FOSUser использует таблицу fos_user для идентификации и проверки учетных данных и ключа, зашифрованного с помощью sha512.

Можно ли изменить или расширить некоторые классы, чтобы в случае, если он не найдет пользователя в таблице fos_user, искать его в таблице пользователей, в которой ключ зашифрован с помощью md5?

Обновление в соответствии с закатом мадшверо:

Я создал пользователя класс:

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

Конечно, я также изменил сервисы.yml для добавления приложения службы.webservice_user_provider: услуги: приложение.форма.группа: класс: AppBundle\Форма\Тип групповой формы теги: - {имя: форма.тип, псевдоним: app_group_регистрация}

    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] просьба.ИНФОРМАЦИЯ: Согласованный маршрут "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. [] []
Author: Gersón Vizquel, 2017-02-15

2 answers

Вы можете сделать это, создав пользовательский поставщик, который проверяет пользователя в обеих таблицах и возвращает найденного пользователя.

Затем вы можете обновить app/config/security.yml, чтобы использовать своего провайдера вместо того, который предоставляет FOSUserBundle:

security:
    providers:
        fos_userbundle:
            id: the.id.of.your.provider
 0
Author: madshvero, 2017-02-15 14:08:58

Прочитав документацию, я смог понять логику методов аутентификации, и мне показалось более удобным и простым использовать для моего проекта поставщика аутентификации из базы данных. На самом деле это очень простое решение, которое описано в: Как загрузить пользователей безопасности из базы данных (поставщик сущностей)

В моем случае для использования FOSUserBundle есть два соображения:

  1. У них должно существовать два метода аутентификации: один предоставлено FOSUserBundle и тем, которое предоставлено MyBundle.
  2. Процесс аутентификации должен пытаться аутентифицировать пользователя обоими способами.

Для этого, помимо рекомендаций, приведенных в Как загрузить пользователей безопасности из базы данных (поставщик сущностей), необходимо изменить некоторые разделы безопасности.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

Вот и все. Я надеюсь, что в будущем кто-нибудь сможет воспользоваться этим постом. Большое спасибо мадшверо за его руководство.

 0
Author: Gersón Vizquel, 2017-02-17 20:25:03