Как создать "две" (чат и администратор) или более защищенных областей с помощью 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
, почему это так? Я схожу с ума!!
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');
}
}
Проблема может быть в маршрутизации. Поскольку в обоих случаях вы используете только один из 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