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
来源:https://stackoverflow.com/questions/34756244/how-to-create-two-chat-and-admin-or-more-secured-areas-with-fosuserbundle