Symfony 5.4 REST API中重写BadCredentialsException异常类
在Symfony 5.4中替换默认的BadCredentialsException为自定义异常类
你想用装饰器实现的思路走不通,因为BadCredentialsException并不是Symfony容器中的服务,它是在认证过程中被直接实例化抛出的,装饰器模式只适用于容器管理的服务。下面给你两种可行的实现方案:
方案一:通过KernelException事件监听器替换异常
这种方式无需改动认证逻辑,只需要在异常抛出后捕获并替换,适合快速修改异常信息的场景。
- 创建事件监听器类:
<?php declare(strict_types=1); namespace App\EventListener; use App\Exception\BadCredentialsException; use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Symfony\Component\HttpKernel\Event\ExceptionEvent; use Symfony\Component\Security\Core\Exception\BadCredentialsException as OriginalBadCredentialsException; class BadCredentialsExceptionSubscriber implements EventSubscriberInterface { public static function getSubscribedEvents(): array { return [ ExceptionEvent::class => 'onKernelException', ]; } public function onKernelException(ExceptionEvent $event): void { $exception = $event->getThrowable(); // 捕获原异常并替换为自定义异常 if ($exception instanceof OriginalBadCredentialsException) { $customException = new BadCredentialsException($exception->getMessageKey(), $exception->getCode(), $exception); $event->setThrowable($customException); } } }
- 注册监听器(Symfony 5.4支持自动发现注解,也可手动在
services.yaml配置):
手动配置的话,在config/services.yaml添加:
services: App\EventListener\BadCredentialsExceptionSubscriber: tags: - { name: kernel.event_subscriber }
方案二:重写认证提供者,直接抛出自定义异常
这种方式更彻底,直接在认证逻辑里抛出你的自定义异常,适合需要对异常做更多自定义逻辑的场景。
- 继承原DaoAuthenticationProvider:
<?php declare(strict_types=1); namespace App\Security; use App\Exception\BadCredentialsException; use Symfony\Component\Security\Core\Exception\AuthenticationException; use Symfony\Component\Security\Core\User\UserCheckerInterface; use Symfony\Component\Security\Core\User\UserInterface; use Symfony\Component\Security\Core\User\UserProviderInterface; use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface; use Symfony\Component\Security\Core\Authentication\Provider\DaoAuthenticationProvider; use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken; class CustomDaoAuthenticationProvider extends DaoAuthenticationProvider { public function __construct( UserProviderInterface $userProvider, UserCheckerInterface $userChecker, string $providerKey, UserPasswordEncoderInterface $passwordEncoder, bool $hideUserNotFoundExceptions = true ) { parent::__construct($userProvider, $userChecker, $providerKey, $passwordEncoder, $hideUserNotFoundExceptions); } /** * {@inheritdoc} */ protected function checkAuthentication(UserInterface $user, UsernamePasswordToken $token): void { try { parent::checkAuthentication($user, $token); } catch (AuthenticationException $e) { // 如果是原BadCredentialsException,替换成自定义的 if ($e instanceof \Symfony\Component\Security\Core\Exception\BadCredentialsException) { throw new BadCredentialsException($e->getMessageKey(), $e->getCode(), $e); } throw $e; } } }
- 替换默认的认证提供者服务:
在config/services.yaml中添加(注意替换成你项目的实际配置参数):
services: Symfony\Component\Security\Core\Authentication\Provider\DaoAuthenticationProvider: class: App\Security\CustomDaoAuthenticationProvider arguments: $userProvider: '@security.user.provider.concrete.app_user_provider' # 你的用户提供者服务ID $userChecker: '@security.user_checker' $providerKey: '%security.firewalls.main.provider%' # 你的防火墙对应的provider名称 $passwordEncoder: '@security.password_encoder' $hideUserNotFoundExceptions: true
内容的提问来源于stack exchange,提问作者Yohann Daniel Carter
相关产品推荐
相关产品推荐

