如何在Symfony 6(EasyAdmin 4)中实现用户账号封禁?
Symfony 6 + EasyAdmin 4 实现用户账号封禁功能
问题背景
我在Symfony 6和EasyAdmin 4环境下想实现网站用户账号封禁功能,一开始尝试创建ROLE_BLOCKED角色,打算在控制器里用类似IsDenied的函数限制访问,但发现Symfony 6里找不到这个函数。
相关代码
LoginAuthenticator.php
class LoginAuthenticator extends AbstractLoginFormAuthenticator { use TargetPathTrait; public const LOGIN_ROUTE = 'app_login'; public function __construct(private UrlGeneratorInterface $urlGenerator) { } public function authenticate(Request $request): Passport { $email = $request->request->get('email', ''); $request->getSession()->set(Security::LAST_USERNAME, $email); return new Passport( new UserBadge($email), new PasswordCredentials($request->request->get('password', '')), [ new CsrfTokenBadge('authenticate', $request->request->get('_csrf_token')), ] ); } public function onAuthenticationSuccess(Request $request, TokenInterface $token, string $firewallName): ?Response { if ($targetPath = $this->getTargetPath($request->getSession(), $firewallName)) { return new RedirectResponse($targetPath); } // For example: // return new RedirectResponse($this->urlGenerator->generate('some_route')); throw new \Exception('TODO: provide a valid redirect inside '.__FILE__); } protected function getLoginUrl(Request $request): string { return $this->urlGenerator->generate(self::LOGIN_ROUTE); } }
security.yaml
# https://symfony.com/doc/current/security.html#registering-the-user-hashing-passwords password_hashers: Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface: 'auto' # https://symfony.com/doc/current/security.html#loading-the-user-the-user-provider providers: # used to reload user from session & other features (e.g. switch_user) app_user_provider: entity: class: App\Entity\User property: email # used to reload user from session & other features (e.g. switch_user) firewalls: dev: pattern: ^/(_(profiler|wdt)|css|images|js)/ security: false main: lazy: true provider: app_user_provider custom_authenticator: App\Security\RegisterAuthenticator logout: path: app_logout # where to redirect after logout # target: app_any_route # activate different ways to authenticate # https://symfony.com/doc/current/security.html#the-firewall # https://symfony.com/doc/current/security/impersonating_user.html # switch_user: true role_hierarchy: ROLE_ADMIN: ROLE_USER ROLE_ARTIST: ROLE_USER # Easy way to control access for large sections of your site # Note: Only the *first* access control that matches will be used access_control: - { path: ^/admin, roles: ROLE_ADMIN } - { path: ^/profile, roles: ROLE_USER } when@test: security: password_hashers: # By default, password hashers are resource intensive and take time. This is # important to generate secure password hashes. In tests however, secure hashes # are not important, waste resources and increase test times. The following # reduces the work factor to the lowest possible values. Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface: algorithm: auto cost: 4 # Lowest possible value for bcrypt time_cost: 3 # Lowest possible value for argon memory_cost: 10 # Lowest possible value for argon
解决方案:使用UserChecker类
通过自定义UserChecker类可以完美解决账号封禁的问题,具体步骤如下:
给User实体添加封禁状态字段
先在App\Entity\User中添加isBlocked布尔字段,用于标记用户是否被封禁:// src/Entity/User.php use Doctrine\ORM\Mapping as ORM; class User implements UserInterface, PasswordAuthenticatedUserInterface { // ... 已有字段 #[ORM\Column(type: 'boolean')] private bool $isBlocked = false; // 生成对应的getter和setter public function isBlocked(): bool { return $this->isBlocked; } public function setIsBlocked(bool $isBlocked): self { $this->isBlocked = $isBlocked; return $this; } // ... 已有方法 }执行数据库迁移命令更新表结构:
php bin/console make:migration php bin/console doctrine:migrations:migrate创建自定义UserChecker类
在src/Security目录下新建UserChecker.php:// src/Security/UserChecker.php use Symfony\Component\Security\Core\User\UserCheckerInterface; use Symfony\Component\Security\Core\User\UserInterface; use Symfony\Component\Security\Core\Exception\LockedException; class UserChecker implements UserCheckerInterface { public function checkPreAuth(UserInterface $user): void { if (!$user instanceof \App\Entity\User) { return; } // 校验用户是否被封禁 if ($user->isBlocked()) { throw new LockedException('你的账号已被封禁,请联系管理员。'); } } public function checkPostAuth(UserInterface $user): void { // 可在此添加登录后的额外校验逻辑,比如账号过期等 } }在security.yaml中配置启用UserChecker
在main防火墙配置中添加user_checker项:firewalls: main: # ... 已有配置 user_checker: App\Security\UserChecker在EasyAdmin中添加封禁/解封操作(可选)
可以在User的CRUD控制器中添加自定义按钮,让管理员快速管理用户封禁状态:// src/Controller/Admin/UserCrudController.php use EasyCorp\Bundle\EasyAdminBundle\Config\Action; use EasyCorp\Bundle\EasyAdminBundle\Config\Actions; use EasyCorp\Bundle\EasyAdminBundle\Context\AdminContext; use EasyCorp\Bundle\EasyAdminBundle\Controller\AbstractCrudController; use Symfony\Component\HttpFoundation\RedirectResponse; class UserCrudController extends AbstractCrudController { // ... 已有方法 public function configureActions(Actions $actions): Actions { $toggleBlock = Action::new('toggleBlock', '封禁/解封') ->linkToCrudAction('toggleBlockAction'); return $actions ->add(Action::INDEX, $toggleBlock) ->add(Action::DETAIL, $toggleBlock); } public function toggleBlockAction(AdminContext $context): RedirectResponse { /** @var \App\Entity\User $user */ $user = $context->getEntity()->getInstance(); $user->setIsBlocked(!$user->isBlocked()); $this->getDoctrine()->getManager()->flush(); $this->addFlash('success', sprintf('用户%s已%s', $user->getEmail(), $user->isBlocked() ? '封禁' : '解封')); return $this->redirect($this->generateUrl('admin', [ 'crudAction' => 'index', 'entityFqcn' => \App\Entity\User::class, ])); } }
配置完成后,被封禁的用户尝试登录时会直接抛出LockedException,Symfony会自动跳转至登录页面并显示错误提示;管理员则可以在EasyAdmin后台便捷地管理用户的封禁状态。
内容的提问来源于stack exchange,提问作者yaboiii23
相关产品推荐
相关产品推荐

