如何用FOSUserBundle创建 "两个"(聊天和管理)或更多的安全区域[英] How to create "two" (chat and admin) or more secured areas with FOSUserBundle

本文是小编为大家收集整理的关于如何用FOSUserBundle创建 "两个"(聊天和管理)或更多的安全区域的处理/解决方法,可以参考本文帮助大家快速定位并解决问题,中文翻译不准确的可切换到English标签页查看源文。

问题描述

我正在Symfony 2.8.x应用程序中工作,我需要设置两个有担保区域:chat和admin.这意味着chat和admin将使用相同的登录模板(如果可能的话,我不需要为此目的设置不同的登录模板).在此处询问之前,我已经搜索过 google ,有一些相关的内容,我读了很多有关此主题的文章: 1 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上覆盖了登录模板. (如果需要来源,我只能提供不超过帖子比已经更长的帖子).

当我调用URL:http://domain.tld/app_dev.php/admin并尝试登录时,我会收到此错误:

找不到翻译. 上下文:{" ID": " Symfony \ Component \ Security \ Security \ Core \异常\ BadCredentialSexception: 不好的信用史.在 /var/www/html/platform-cm/vendor/symfony/symfony/symfony/symfony/symfony/component/security/core/core/authentication/authentication/provider/userauthicationalicationprovider.php:90 \ nstack 跟踪:\ n#0

(如果需要,我可以提供完整的堆叠)

这对我来说很奇怪,但也许是由于我有双重检查的凭据而导致了不良配置.

当我调用url:http://domain.tld/app_dev.php/chat并尝试登录时,我得到了Access Denied,但这是正确的,因为我被重定向到http://domain.tld/app_dev.php/admin/.可以在这种配置方面给我一些帮助吗?我被困并且由于这个

而无法前进

第二方法

这是使用侦听器在@heah建议上提出的第二种方法,但我仍然无法正常工作,我仍然收到相同的"不良凭据"消息,根本无法登录.我已经更改了routing.yml:

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

我已经更改了security.yml:

安全: ... 防火墙: 行政: 模式: ^/ 匿名:〜 form_login: 提供商:fos_userbundle CSRF_PROVIDER:Security.csrf.token_manager login_path:fos_user_security_login check_path: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 }

然后,我为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);
    }

}

我遇到了同样的问题,也许我做错了什么,我没有看到,但我接受了任何想法.这里有什么问题?

3rd方法

我对我的代码进行了审查,并在@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的用户登录时,由于我得到了访问的例外,一切似乎都可以正常工作,但是当我尝试以ROLE_ADMIN的用户登录时,它会停止工作,然后回到初始错误:Bad credentials , 为什么是这样?我发疯了!

推荐答案

您需要启用翻译:

# config.yml

framework:
    translator: ~
...

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

参见/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 }

请参阅/authentication.html

您也应该使用相同的防火墙,因为它们共享相同的配置,并且已经根据用户角色定义了访问控件.

您只需要为'/'as创建一个核心控制器:

# 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');
    }
}

其他推荐答案

问题可能在路由中.由于在这两种情况下,您仅使用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

本文地址:https://www.itbaoku.cn/post/2090791.html

问题描述

I'm working in a Symfony 2.8.x app and I need to setup two secured areas: chat and admin. This means that chat and admin will use the same login template (if this is possible and I don't need to setup different one for this purpose). I have googled before ask here and there is a few things related showing up and I read a lot of post about this topic: 1, 2, 3, 4 just as an example of them but I am doing something wrong since I can't get this working properly. This is how /app/config/security.yml looks like (just the firewalls and access_control piece of code):

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 }

Now this is the config for my bundles at 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

And for FOSUserBundle I have tried this two (both without success and each at a time):

#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

I have overwrite the login template at app/Resources/FOSUserBundle/views/Security/login.html.twig. (if source is needed I can provide just ommit for not make the post longer than it's already).

When I call the URL: http://domain.tld/app_dev.php/admin and try to login I got this error:

Translation not found. Context: { "id": "Symfony\Component\Security\Core\Exception\BadCredentialsException: Bad credentials. in /var/www/html/platform-cm/vendor/symfony/symfony/src/Symfony/Component/Security/Core/Authentication/Provider/UserAuthenticationProvider.php:90\nStack trace:\n#0

(I can provide the full stacktrace if needed)

this is weird to me but perhaps it's caused for a bad configuration since I have double checked credentials.

When I call the URL: http://domain.tld/app_dev.php/chat and try to login I got Access Denied but it's right because I am redirected to http://domain.tld/app_dev.php/admin/. Can any give me some help on this configuration? I am stuck and can't move forward because of this

2nd approach

This is a second approach bassed on @heah suggestion using a listener but is not working too I am still getting the same "Bad credentials" message and can't login at all. I have changed back the routing.yml to this:

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

I have changed back the security.yml to this:

security: ... firewalls: admin: pattern: ^/ anonymous: ~ form_login: provider: fos_userbundle csrf_provider: security.csrf.token_manager login_path: fos_user_security_login check_path: 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 }

Then I have defined a listener for the event security.interactive_login as suggested in app/config/config.yml:

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

Then at 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 }

And finally here is the class definition at 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);
    }

}

I am running the same issue, again, perhaps I am doing something wrong and I am not seeing but I accept any ideas. What could be wrong here?

3rd approach

I have take a review to my code and change it slighty mostly following @heah suggestions. So, now security.yml is as follow:

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 }

the changes at services.yml are basically fix the arguments since security.context has been split in Symfony 2.6+ although I am not using it at all:

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

And lastly the changes at the class 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'
            );
        }
    }
}

When I login as user with ROLE_CHATTER everything seems to work since I got the AccessDenied exception but when I try to login as a user with ROLE_ADMIN it stop working and I come back to the initial error: Bad credentials, why is this? I am getting crazy!!

推荐答案

You need to enable translation :

# config.yml

framework:
    translator: ~
...

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

see 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 }

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

Also you should use the same firewall since they share the same configuration and you already define access controls based on user role.

You would only need to create a core controller for '/' as :

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

and

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');
    }
}

其他推荐答案

The issue may be in the routing. Since in both cases your are using only one from FOSUserBundle to authenticate, you should try to create two different routes one for each firewall, example:

#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