You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Symfony 5.4 REST API中重写BadCredentialsException异常类

在Symfony 5.4中替换默认的BadCredentialsException为自定义异常类

你想用装饰器实现的思路走不通,因为BadCredentialsException并不是Symfony容器中的服务,它是在认证过程中被直接实例化抛出的,装饰器模式只适用于容器管理的服务。下面给你两种可行的实现方案:

方案一:通过KernelException事件监听器替换异常

这种方式无需改动认证逻辑,只需要在异常抛出后捕获并替换,适合快速修改异常信息的场景。

  1. 创建事件监听器类:
<?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);
        }
    }
}
  1. 注册监听器(Symfony 5.4支持自动发现注解,也可手动在services.yaml配置):
    手动配置的话,在config/services.yaml添加:
services:
    App\EventListener\BadCredentialsExceptionSubscriber:
        tags:
            - { name: kernel.event_subscriber }

方案二:重写认证提供者,直接抛出自定义异常

这种方式更彻底,直接在认证逻辑里抛出你的自定义异常,适合需要对异常做更多自定义逻辑的场景。

  1. 继承原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;
        }
    }
}
  1. 替换默认的认证提供者服务:
    在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.17 12:45:31