Installation Add via Composer:
composer require jms/security-extra-bundle
Enable in config/bundles.php:
return [
// ...
JMS\SecurityExtraBundle\JMSSecurityExtraBundle::class => ['all' => true],
];
First Use Case: Role-Based Access Control (RBAC)
Secure a controller method with @Security("has_role('ROLE_ADMIN')"):
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security;
class AdminController extends Controller
{
/**
* @Security("has_role('ROLE_ADMIN')")
*/
public function dashboard()
{
return $this->render('admin/dashboard.html.twig');
}
}
Key Configuration
Update config/packages/security.yaml:
security:
access_control:
- { path: ^/admin, roles: ROLE_ADMIN }
# Enable annotations (if using SensioFrameworkExtraBundle)
enable_authenticator_manager: true
Annotation-Based Security
@Security for granular control.
/**
* @Security("has_role('ROLE_EDITOR') or has_role('ROLE_ADMIN')")
*/
public function edit(Post $post) { ... }
/**
* @Security("has_role('ROLE_USER')")
*/
class UserController extends Controller { ... }
Voter Integration
PostVoter) and register them in security.yaml:
security:
access_decision_manager:
strategy: affirmative
voters:
- JMS\SecurityExtraBundle\Security\Authorization\Voter\PostVoter
/**
* @Security("is_granted('EDIT', post)")
*/
public function edit(Post $post) { ... }
CSRF Protection
security.yaml:
security:
csrf_protection:
enabled: true
@CSRFProtect:
/**
* @CSRFProtect(false)
*/
public function apiWebhook() { ... }
Two-Factor Authentication (2FA)
JMS\SecurityExtraBundle\Security\Authentication\Token\TwoFactorToken for custom 2FA flows.AbstractGuardAuthenticator to handle 2FA tokens.Event Listeners
security.interactive_login or security.authentication.success:
public function onAuthenticationSuccess(AuthenticationSuccessEvent $event)
{
$user = $event->getAuthenticationToken()->getUser();
// Log or trigger actions (e.g., send welcome email)
}
services.yaml:
services:
App\EventListener\SecurityListener:
tags:
- { name: kernel.event_listener, event: security.authentication.success }
Symfony Flex Compatibility
AppKernel.php.Doctrine Integration
JMS\SecurityExtraBundle\Security\Authorization\Voter\ORMVoter for object-level permissions:
/**
* @Security("is_granted('DELETE', post)")
*/
public function delete(Post $post) { ... }
@ORM\HasLifecycleCallbacks).API Security
lexik/jwt-authentication-bundle for token-based auth:
security:
access_control:
- { path: ^/api, roles: IS_AUTHENTICATED_FULLY }
Testing
SecurityContext in PHPUnit:
$token = new UsernamePasswordToken($user, null, 'main', $user->getRoles());
$this->container->get('security.token_storage')->setToken($token);
Annotation Parsing Issues
@Security annotations ignored.sensio/framework-extra-bundle is installed and annotations: true in framework.yaml:
framework:
router:
utf8: true
annotations: true
Voter Precedence
access_decision_manager to define strategy (e.g., unanimous for strict checks):
security:
access_decision_manager:
strategy: unanimous
CSRF Token Mismatch
InvalidCsrfTokenException in forms.@CSRFToken in Twig:
{{ form_start(form, { attr: { 'data-csrf-token': app.request.csrfToken } }) }}
Deprecated Features
JMS\SecurityExtraBundle\Security\Authorization\Voter\RoleVoter is deprecated.RoleVoter or migrate to custom voters.Performance with ORM Voters
isGranted('EDIT', entity).@ORM\QueryAnnotation or DQL in voters:
public function supportsAttribute($attribute, $subject)
{
return $attribute === 'EDIT' && $subject instanceof Post;
}
public function vote(AuthenticationToken $token, $subject, array $attributes)
{
$user = $token->getUser();
return $user->getId() === $subject->getAuthorId();
}
Enable Security Debugging
config/packages/dev/security.yaml:
security:
debug: true
SECURITY level messages.Dump Voters
public function debugVoters()
{
$dm = $this->container->get('security.access_decision_manager');
dump($dm->getDecisionManagers());
}
Test Voters in Isolation
AccessDecisionManager directly:
$dm = $this->container->get('security.access_decision_manager');
$token = new UsernamePasswordToken($user, null, 'main', ['ROLE_USER']);
$result = $dm->decide($token, ['ROLE_ADMIN'], ['POST']);
Custom Voters
AbstractVoter:
class CustomVoter extends AbstractVoter
{
public function supportsAttribute($attribute)
{
return $attribute === 'CUSTOM_ACTION';
}
protected function voteOnAttribute($attribute, $subject, TokenInterface $token)
{
return AccessDecisionManagerInterface::ACCESS_GRANTED;
}
}
Override Access Control
AccessControlList:
security:
access_control:
- { path: ^/custom, roles: ROLE_CUSTOM }
Event Subscribers
SecurityEvents (e.g., AUTHENTICATION_SUCCESS):
public static function getSubscribedEvents()
{
return [
SecurityEvents::AUTHENTICATION_SUCCESS => 'onAuthenticationSuccess',
];
}
Token Factories
TokenFactoryInterface for custom authentication tokens:
class TwoFactorTokenFactory implements TokenFactoryInterface
{
public function createToken(UserInterface $user, string $firewallName, array $options)
{
return new TwoFactorToken($user, $firewallName, $options);
}
}
How can I help you explore Laravel packages today?