Symfony首次登录成功后二次登录验证失败求助
Symfony二次登录失败(BadCredentialsException)排查与解决
问题现象
Symfony项目中实现了LoginController和RegistrationController,用户注册后首次登录正常,但注销后二次登录抛出Symfony\Component\Security\Core\Exception\BadCredentialsException,提示密码无效。该问题在phpMyAdmin数据库崩溃、通过Symfony重新生成数据库后出现,怀疑与ROLE_USER角色配置有关,已排查密码哈希逻辑、角色配置和登录实现未发现问题。
错误日志
Symfony\Component\Security\Core\Exception\BadCredentialsException {#196 ▼ #message: "The presented password is invalid." #code: 0 #file: "C:\xampp\htdocs\PROJET_SYMFONY\Ventalis\restaurant - 14122023\vendor\symfony\security-http\EventListener\CheckCredentialsListener.php" #line: 69 #serialized: null -token: null trace: {▼ C:\xampp\htdocs\PROJET_SYMFONY\Ventalis\restaurant - 14122023\vendor\symfony\security-http\EventListener\CheckCredentialsListener.php:69 {▶} C:\xampp\htdocs\PROJET_SYMFONY\Ventalis\restaurant - 14122023\vendor\symfony\http-foundation\Session\Storage\NativeSessionStorage.php:175 {▶} C:\xampp\htdocs\PROJET_SYMFONY\Ventalis\restaurant - 14122023\vendor\symfony\http-foundation\Session\Storage\NativeSessionStorage.php:326 {▶} C:\xampp\htdocs\PROJET_SYMFONY\Ventalis\restaurant - 14122023\vendor\symfony\http-foundation\Session\Session.php:258 {▶} C:\xampp\htdocs\PROJET_SYMFONY\Ventalis\restaurant - 14122023\vendor\symfony\http-foundation\Session\Session.php:278 {▶} C:\xampp\htdocs\PROJET_SYMFONY\Ventalis\restaurant - 14122023\vendor\symfony\http-foundation\Session\Session.php:70 {▶} C:\xampp\htdocs\PROJET_SYMFONY\Ventalis\restaurant - 14122023\vendor\symfony\security-http\Authentication\AuthenticationUtils.php:40 {▶} C:\xampp\htdocs\PROJET_SYMFONY\Ventalis\restaurant - 14122023\src\Controller\LoginController.php:16 {▶} C:\xampp\htdocs\PROJET_SYMFONY\Ventalis\restaurant - 14122023\vendor\symfony\http-kernel\HttpKernel.php:163 {▶} C:\xampp\htdocs\PROJET_SYMFONY\Ventalis\restaurant - 14122023\vendor\symfony\http-kernel\HttpKernel.php:75 {▶} C:\xampp\htdocs\PROJET_SYMFONY\Ventalis\restaurant - 14122023\vendor\symfony\http-kernel\Kernel.php:202 {▶} C:\xampp\htdocs\PROJET_SYMFONY\Ventalis\restaurant - 14122023\vendor\symfony\runtime\Runner\Symfony\HttpKernelRunner.php:35 {▶} C:\xampp\htdocs\PROJET_SYMFONY\Ventalis\restaurant - 14122023\vendor\autoload_runtime.php:29 {▶} C:\xampp\htdocs\PROJET_SYMFONY\Ventalis\restaurant - 14122023\public\index.php:5 {▶}` } }
注册控制器代码
<?php namespace App\Controller; use App\classes\Mail; use App\Entity\User; use App\Security\EmailVerifier; use App\Form\RegistrationFormType; use Symfony\Component\Mime\Address; use Doctrine\ORM\EntityManagerInterface; use Symfony\Bridge\Twig\Mime\TemplatedEmail; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Annotation\Route; use Symfony\Contracts\Translation\TranslatorInterface; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface; use SymfonyCasts\Bundle\VerifyEmail\Exception\VerifyEmailExceptionInterface; use Symfony\Component\Mailer\MailerInterface; class RegistrationController extends AbstractController { private EmailVerifier $emailVerifier; public function __construct(EmailVerifier $emailVerifier) { $this->emailVerifier = $emailVerifier; } #[Route('/register', name: 'app_register')] public function register(Request $request, UserPasswordHasherInterface $userPasswordHasher, EntityManagerInterface $entityManager): Response { $user = new User(); $form = $this->createForm(RegistrationFormType::class, $user); $form->handleRequest($request); if ($form->isSubmitted() && $form->isValid()) { // 加密明文密码 $user->setPassword( $userPasswordHasher->hashPassword( $user, $form->get('plainPassword')->getData() ) ); $entityManager->persist($user); $entityManager->flush(); // 生成签名URL并发送给用户 // flash消息显示一次后消失,需在Twig中渲染 $mail = new Mail(); $isSent = $mail->send($user->getEmail(), $user->getFirstname(), '邮箱地址验证', 'blabla'); if ($isSent) { $this->addFlash('success', '您将收到一封注册确认邮件'); } // return $this->redirectToRoute('app_home'); } return $this->render('registration/register.html.twig', [ 'registrationForm' => $form->createView(), ]); } }
登录控制器代码
<?php namespace App\Controller; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Annotation\Route; use Symfony\Component\Security\Http\Authentication\AuthenticationUtils; class LoginController extends AbstractController { #[Route('/login', name: 'app_login')] public function index(AuthenticationUtils $authenticationUtils): Response { // 获取登录错误(如果有) $error = $authenticationUtils->getLastAuthenticationError(); dump($error); // 调试用 // 用户上次输入的用户名 $lastUsername = $authenticationUtils->getLastUsername(); return $this->render('login/index.html.twig', [ 'last_username' => $lastUsername, 'error' => $error, ]); } }
security.yaml配置
security: # 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: # 用于从会话重新加载用户及其他功能(如switch_user) app_user_provider: entity: class: App\Entity\User property: email firewalls: dev: pattern: ^/(_(profiler|wdt)|css|images|js)/ security: false main: lazy: true provider: app_user_provider form_login: # "app_login"是之前创建的路由名称 login_path: app_login check_path: app_login enable_csrf: true logout: path: app_logout # 启用不同的认证方式 # https://symfony.com/doc/current/security.html#the-firewall # https://symfony.com/doc/current/security/impersonating_user.html # switch_user: true # 便捷控制网站大片区域的访问权限 # 注意:只有第一个匹配的access control会生效 role_hierarchy: ROLE_EMPLOYEE: ROLE_USER ROLE_ADMIN: ROLE_EMPLOYEE ROLE_SUPER_ADMIN: [ROLE_ADMIN, ROLE_ALLOWED_TO_SWITCH] access_control: #- { path: ^/app/login$, roles: IS_AUTHENTICATED_ANONYMOUSLY } # - { path: ^/product, roles: ROLE_ADMIN } # - { path: ^/profile, roles: ROLE_USER } # - { path: ^/admin-employe, roles: ROLE_EMPLOYEE } when@test: security: password_hashers: # 默认情况下,密码哈希算法资源消耗大、耗时久,这对生成安全密码哈希很重要。 # 但在测试环境中,安全哈希不重要,反而浪费资源、增加测试时间。 # 以下配置将工作因子设为最低可能值。 Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface: algorithm: auto cost: 4 # bcrypt最低可能值 time_cost: 3 # argon最低可能值 memory_cost: 10 # argon最低可能值
排查与解决方向
- 会话存储问题:注销后会话未完全清除,导致二次登录时会话中残留的旧认证信息干扰新请求。检查
security.yaml中logout配置是否显式声明invalidate_session: true,并手动清除浏览器缓存和Cookie测试。 - 用户实体序列化问题:重新生成数据库后,用户实体序列化逻辑可能异常。确保User实体实现
Serializable接口,或配置正确的序列化组,保证从会话加载用户时数据完整。 - 密码哈希器一致性:显式指定哈希算法(如
bcrypt),避免auto模式下重新生成数据库后算法变化,导致旧哈希无法验证。 - 用户角色存储问题:检查User实体中
roles字段的类型(需为Doctrine的json或array类型),且默认值包含ROLE_USER,确保角色数据存储正常。 - 缓存清理:执行命令清除Symfony缓存,避免残留旧配置:
php bin/console cache:clear php bin/console cache:clear --env=prod # 生产环境执行
内容的提问来源于stack exchange,提问作者Lea
相关产品推荐
相关产品推荐

