Как создать "две" (чат и администратор) или более защищенных областей с помощью FOSUserBundle


Я работаю в приложении Symfony 2.8.x, и мне нужно настроить две защищенные области: chat и admin. Это означает, что chat и admin будут использовать один и тот же шаблон входа (если это возможно, и мне не нужно настраивать другой для этой цели). Я погуглил, прежде чем спросить здесь, и есть несколько вещей, связанных с появлением, и я прочитал много сообщений на эту тему: 1, 2, 3, 4 просто в качестве примера из них, но я кое-что делаю неправильно, так как я не могу заставить это работать должным образом. Вот как выглядит /app/config/security.yml (только брандмауэры и фрагмент кода access_control):

security:
    ....
    firewalls:
        admin:
            pattern: /admin/(.*)
            anonymous: ~
            form_login:
                provider: fos_userbundle
                csrf_provider: security.csrf.token_manager
                login_path: fos_user_security_login
                check_path: fos_user_security_check  
                use_forward: true                                           
                always_use_default_target_path: true
                default_target_path: /admin
                target_path_parameter: _target_path
                use_referer: true
                remember_me: true
            logout:
              target: /admin
            remember_me:
                secret:   '%secret%'
                lifetime: 604800 # 1 week in seconds
                path:     /
        chat:
            pattern: ^/chat/(.*)
            anonymous: ~
            form_login:
                provider: fos_userbundle
                csrf_provider: security.csrf.token_manager
                login_path: fos_user_security_login
                check_path: fos_user_security_check
                use_forward: true
                always_use_default_target_path: true
                default_target_path: /chat
                target_path_parameter: _target_path
                use_referer: true    
                remember_me: true
            logout: ~
            remember_me:
                secret:   '%secret%'
                lifetime: 604800 # 1 week in seconds
                path:     /

    access_control:
        - { path: ^/admin/login$, role: IS_AUTHENTICATED_ANONYMOUSLY }
        - { path: ^/admin/resetting$, role: IS_AUTHENTICATED_ANONYMOUSLY }
        - { path: ^/admin/logout$, role: IS_AUTHENTICATED_ANONYMOUSLY }

        - { path: ^/chat/login$, role: IS_AUTHENTICATED_ANONYMOUSLY }
        - { path: ^/chat/resetting$, role: IS_AUTHENTICATED_ANONYMOUSLY }
        - { path: ^/chat/logout$, role: IS_AUTHENTICATED_ANONYMOUSLY }

        - { path: ^/chat/, role: ROLE_CHATTER }
        - { path: ^/admin/, role: ROLE_ADMIN }

Теперь это конфигурация для моих пакетов на app/config/routing.yml:

platform_chat:
    resource: "@PlatformChatBundle/Controller/"
    type:     annotation
    prefix:   /chat/
    options:
            expose: true

platform_admin:
    resource: "@PlatformAdminBundle/Controller/"
    type:     annotation
    prefix:   /admin/
    options:
        expose: true

И для FOSUserBundle Я попробовал эти два (оба безуспешно и каждый по очереди):

#FOSUser
fos_user:
    resource: "@FOSUserBundle/Resources/config/routing/all.xml"



#FOSUser    
# this second causes doubts to me since I think I will need 
# to repeat the same routes for chat prefix but I'm not sure at all
fos_user_security:
    resource: "@FOSUserBundle/Resources/config/routing/security.xml"
    prefix: /admin

fos_user_profile:
    resource: "@FOSUserBundle/Resources/config/routing/profile.xml"
    prefix: /admin/profile

fos_user_register:
    resource: "@FOSUserBundle/Resources/config/routing/registration.xml"
    prefix: /admin/register

fos_user_resetting:
    resource: "@FOSUserBundle/Resources/config/routing/resetting.xml"
    prefix: /admin/resetting

fos_user_change_password:
    resource: "@FOSUserBundle/Resources/config/routing/change_password.xml"
    prefix: /admin/profile

Я перезаписал шаблон входа в систему по адресу app/Resources/FOSUserBundle/views/Security/login.html.twig. (если нужен источник, я могу предоставить только ommit, чтобы не делать сообщение длиннее, чем оно уже есть).

Когда я вызываю URL-адрес: http://domain.tld/app_dev.php/admin и при попытке войти в систему я получил эту ошибку:

Перевод не найден. Контекст: {"идентификатор": "Symfony\Компонент\Безопасность\Ядро\Исключение\Исключение BadCredentialsException: Плохие рекомендации. в /var/www/html/platform-cm/vendor/symfony/symfony/src/Symfony/Component/Security/Core/Authentication/Provider/UserAuthenticationProvider.php:90\ Трассировка nstack:\n#0

(При необходимости я могу предоставить полную трассировку стека)

Для меня это странно, но, возможно, это вызвано плохим конфигурация, так как я дважды проверил учетные данные.

Когда я звоню по URL: http://domain.tld/app_dev.php/chat и пытаюсь войти, я получаю Access Denied, но это правильно, потому что я перенаправлен на http://domain.tld/app_dev.php/admin/. Может ли кто-нибудь помочь мне в этой конфигурации? Я застрял и не могу двигаться вперед из-за этого

2-й подход

Это второй подход, основанный на предложении @heah с использованием слушателя, но он тоже не работает. Я все еще получаю то же сообщение "Неверные учетные данные" и вообще не могу войти в систему. Я изменили routing.yml обратно на это:

#FOSUser
fos_user:
    resource: "@FOSUserBundle/Resources/config/routing/all.xml"

Я изменил security.yml на это:

Безопасность: ... брандмауэры: администратор: шаблон: ^/ аноним: ~ форм_логин: поставщик: fos_userbundle csrf_provider: security.csrf.token_manager (менеджер токенов) путь к логину: fos_user_security_login_пользователя check_path: fos_user_security_check

контрольный путь: fos_user_security_check

            # if true, forward the user to the login form instead of redirecting
            use_forward: true

            # login success redirecting options (read further below)
            always_use_default_target_path: true
            default_target_path:            /admin
            target_path_parameter:          _target_path
            use_referer: true
            remember_me:    true
        logout:
          target: /admin
        remember_me:
            secret:   '%secret%'
            lifetime: 604800 # 1 week in seconds
            path:     /

access_control:
    - { path: ^/admin/login$, role: IS_AUTHENTICATED_ANONYMOUSLY }
    - { path: ^/admin/resetting$, role: IS_AUTHENTICATED_ANONYMOUSLY }
    - { path: ^/admin/logout$, role: IS_AUTHENTICATED_ANONYMOUSLY }

    - { path: ^/chat/login$, role: IS_AUTHENTICATED_ANONYMOUSLY }
    - { path: ^/chat/resetting$, role: IS_AUTHENTICATED_ANONYMOUSLY }
    - { path: ^/chat/logout$, role: IS_AUTHENTICATED_ANONYMOUSLY }

    - { path: ^/chat/, role: ROLE_CHATTER }
    - { path: ^/admin/, role: ROLE_ADMIN }

Затем я определил слушателя для событие security.interactive_login, как предложено в app/config/config.yml:

parameters:
    account.security_listener.class: PlatformAdminBundle\Listener\SecurityListener

Затем в app/config/services.yml:

services:
    account.security_listener:
        class: %account.security_listener.class%
        arguments: ['@security.context', '@session']
        tags:
            - { name: kernel.event_listener, event: security.interactive_login, method: onSecurityInteractiveLogin }

И, наконец, вот определение класса в src/PlatformAdminBundle/Listener/SecurityListener.php:

namespace PlatformAdminBundle\Listener;

use Symfony\Component\Security\Core\SecurityContextInterface;
use Symfony\Component\HttpFoundation\Session\Session;
use Symfony\Component\Security\Http\Event\InteractiveLoginEvent;

class SecurityListener
{

    public function __construct(SecurityContextInterface $security, Session $session)
    {
        $this->security = $security;
        $this->session = $session;
    }

    public function onSecurityInteractiveLogin(InteractiveLoginEvent $event)
    {
        $user = $this->security->getToken()->getUser();
        var_export($user);
    }

}

Я снова сталкиваюсь с той же проблемой, возможно, я делаю что-то не так, и я не вижу, но я принимаю любые идеи. Что здесь может быть не так?

3-й подход

Я должен пересмотреть свой код и слегка изменить его, в основном следуя предложениям @heah. Итак, теперь security.yml выглядит так следуйте:

access_control:
    - { path: ^/login$, role: IS_AUTHENTICATED_ANONYMOUSLY }
    - { path: ^/resetting$, role: IS_AUTHENTICATED_ANONYMOUSLY }
    - { path: ^/logout$, role: IS_AUTHENTICATED_ANONYMOUSLY }

    - { path: ^/chat/, role: ROLE_CHATTER }
    - { path: ^/admin/, role: ROLE_ADMIN }

Изменения в services.yml в основном исправляют аргументы, так как security.context был разделен в Symfony 2.6+, хотя я его вообще не использую:

services:
    ...
    account.security_listener:
        class: %account.security_listener.class%
        arguments: ['@security.authorization_checker']
        tags:
            - { name: kernel.event_listener, event: security.interactive_login, method: onSecurityInteractiveLogin }

И, наконец, изменения в классе PlatformAdminBundle/Listener/SecurityListener.php:

namespace Clanmovil\PlatformAdminBundle\Listener;

use Symfony\Component\Finder\Exception\AccessDeniedException;
use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
use Symfony\Component\Security\Http\Event\InteractiveLoginEvent;

class SecurityListener
{

    public function __construct(AuthorizationCheckerInterface $authorizationChecker)
    {
        $this->security = $authorizationChecker;
    }

    public function onSecurityInteractiveLogin(InteractiveLoginEvent $event)
    {
        if ($this->security->isGranted('ROLE_ADMIN')) {
            // this is something for testing
            throw new AccessDeniedException(
                'Access Denied. You are ADMIN'
            );
        } elseif ($this->security->isGranted('ROLE_CHATTER')) {
            // this is something for testing
            throw new AccessDeniedException(
                'Access Denied. You are CHATTER'
            );
        }
    }
}

Когда я вхожу в систему как пользователь с помощью ROLE_CHATTER, кажется, все работает, так как я получил исключение AccessDenied, но когда я пытаюсь войти в систему как пользователь с помощью ROLE_ADMIN, он перестает работать, и я возвращаюсь к первоначальной ошибке: Bad credentials, почему это так? Я схожу с ума!!

Author: Community, 2016-01-13

2 answers

Вам необходимо включить перевод:

# config.yml

framework:
    translator: ~
...

fos_user:
    db_driver: orm # or mongodb|couchdb|propel
    firewall_name: global
    user_class: AppBundle\Entity\User

См. https://symfony.com/doc/master/bundles/FOSUserBundle/index.html

#security.yml

security:
    firewalls:
        # ...
        global:
            pattern:  ^/
            anonymous: true
            provider:  fos_userbundle
            form_login:
                csrf_token_generator: security.csrf.token_manager
                remember_me: true
                default_target_path: root
            logout:
                path: fos_user_security_logout
                target: root
            remember_me:
                secret:   '%secret%'
                lifetime: 604800 # 1 week in seconds

    access_control:
        - { path: ^/admin/login$, role: IS_AUTHENTICATED_ANONYMOUSLY }
        - { path: ^/admin/register, role: IS_AUTHENTICATED_ANONYMOUSLY }
        - { path: ^/admin/resetting, role: IS_AUTHENTICATED_ANONYMOUSLY }

        - { path: ^/chat/, role: ROLE_CHATTER }
        - { path: ^/admin/, role: ROLE_ADMIN }

См. http://symfony.com/doc/current/components/security/authentication.html

Также вы должны использовать один и тот же брандмауэр, так как они имеют одинаковую конфигурацию, и вы уже определяете элементы управления доступом на основе роли пользователя.

Вам нужно будет только создать основной контроллер для '/' как :

# routing.yml
root:
    path: /
    defaults: { _controller: Your\Namespace\Controller\RootController::rootAction }

И

namespace Your\Namespace\Controller;

use Symfony\Bundle\FrameworkBundle\Controller;

class RootControler extends Controller
{
    public function rootAction()
    {
        $security = $this->get('security.authorization_checker');

        if ($security->isGranted('ROLE_ADMIN')) {
            return $this->redirectToRoute('your_admin_root');
        }

        if ($security->isGranted('ROLE_CHATTER')) {
            return $this->redirectToRoute('your_chatter_route');
        }

        return $this->redirectToRoute('fos_user_security_login');
    }
}
 1
Author: Heah, 2016-01-16 03:16:36

Проблема может быть в маршрутизации. Поскольку в обоих случаях вы используете только один из FOSUserBundle для аутентификации, вам следует попытаться создать два разных маршрута по одному для каждого брандмауэра, например:

#FOSUser
fos_user_security:
    resource: "@FOSUserBundle/Resources/config/routing/security.xml"
    prefix: /admin

#FOSUser
fos_user_security:
    resource: "@FOSUserBundle/Resources/config/routing/security.xml"
    prefix: /chat
 0
Author: bitgandtter, 2016-01-15 13:40:36