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

Symfony 6.2登录表单未调用自定义哈希类的问题排查

Symfony自定义密码哈希器登录不生效(命令行正常)

问题概述

在PHP 8.1 + Symfony 6.2项目中,已配置自定义密码哈希类CustomVerySecureHasher并绑定到App\Entity\Useraccount实体:

  • 执行命令php bin/console security:hash-password testPpassword时,自定义哈希类可正常生成MD5哈希
  • 通过GUI登录表单验证时,密码验证流程从未进入自定义哈希类,导致输入密码与数据库哈希值不匹配,无法登录
  • 执行php bin/console debug:config security发现,App\Entity\Useraccount的哈希配置显示hash_algorithm: sha512,与配置的自定义类不符

相关配置与代码

security.yaml

security:
    password_hashers:
        App\Entity\Useraccount:
            id: 'App\Security\Hasher\CustomVerySecureHasher'
    providers:
        users_in_memory: { memory: null }
    firewalls:
        dev:
            pattern: ^/(_(profiler|wdt)|css|images|js)/
            security: false
        main:
            lazy: true
            provider: users_in_memory
            custom_authenticator: App\Security\AppCustomAuthenticator
    access_control:

when@test:
    security:
        password_hashers:
            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

CustomVerySecureHasher类

<?php

namespace App\Security\Hasher;

use Exception;
use Symfony\Component\PasswordHasher\PasswordHasherInterface;

class CustomVerySecureHasher implements PasswordHasherInterface
{
    public function hash(string $plainPassword): string
    {
        if (!in_array('md5', hash_algos(), true)) {
            throw new Exception('MD5 is not supported by this system.');
        }
        return md5($plainPassword);
    }

    public function verify(string $hashedPassword, string $plainPassword): bool
    {
        return $hashedPassword === md5($plainPassword);
    }

    public function needsRehash(string $hashedPassword): bool
    {
        return false;
    }
}

AppCustomAuthenticator类

<?php

namespace App\Security;

use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Security;
use Symfony\Component\Security\Http\Authenticator\AbstractLoginFormAuthenticator;
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\CsrfTokenBadge;
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge;
use Symfony\Component\Security\Http\Authenticator\Passport\Credentials\PasswordCredentials;
use Symfony\Component\Security\Http\Authenticator\Passport\Passport;
use Symfony\Component\Security\Http\Util\TargetPathTrait;

class AppCustomAuthenticator extends AbstractLoginFormAuthenticator
{
    use TargetPathTrait;

    public const LOGIN_ROUTE = 'app_login';

    public function __construct(private UrlGeneratorInterface $urlGenerator)
    {
    }

    public function authenticate(Request $request): Passport
    {
        $login = $request->request->get('login', '');
        $request->getSession()->set(Security::LAST_USERNAME, $login);

        return new Passport(
            new UserBadge($login),
            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);
        }

        return new RedirectResponse($this->urlGenerator->generate('app_home'));
    }

    protected function getLoginUrl(Request $request): string
    {
        return $this->urlGenerator->generate(self::LOGIN_ROUTE);
    }
}

问题原因

核心问题是登录使用的用户提供者是内存提供者users_in_memory,而非绑定自定义哈希器的Useraccount实体提供者:

  1. 自定义哈希器的配置是绑定到App\Entity\Useraccount实体的,只有当Symfony加载该实体类的用户时,才会使用对应的自定义哈希器
  2. 当前防火墙使用users_in_memory提供者,Symfony会对内存用户使用默认的哈希配置(或继承自PasswordAuthenticatedUserInterface的全局配置),因此不会触发自定义哈希类
  3. debug:config显示的hash_algorithm: sha512是Symfony为内存用户 fallback 的默认哈希算法,与你配置的自定义类无关

解决方案

1. 替换用户提供者为实体提供者

修改security.yaml的providers和firewalls.main部分,让Symfony从数据库加载Useraccount实体:

security:
    # ... 其他配置
    providers:
        # 替换原有的users_in_memory
        app_user_provider:
            entity:
                class: App\Entity\Useraccount
                property: login # 替换为你的Useraccount实体中用于登录的字段(如username/email)
    firewalls:
        main:
            lazy: true
            provider: app_user_provider # 使用新的实体提供者
            custom_authenticator: App\Security\AppCustomAuthenticator
    # ... 其他配置

2. 确保Useraccount实体实现必要接口

Useraccount实体必须实现UserInterface和PasswordAuthenticatedUserInterface,并正确实现相关方法:

<?php
namespace App\Entity;

use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface;
use Symfony\Component\Security\Core\User\UserInterface;

#[ORM\Entity(repositoryClass: UseraccountRepository::class)]
class Useraccount implements UserInterface, PasswordAuthenticatedUserInterface
{
    #[ORM\Id]
    #[ORM\GeneratedValue]
    #[ORM\Column(type: 'integer')]
    private ?int $id = null;

    #[ORM\Column(type: 'string', length: 180, unique: true)]
    private ?string $login = null;

    #[ORM\Column(type: 'string')]
    private ?string $password = null;

    // ... 其他字段、getter和setter

    public function getUserIdentifier(): string
    {
        return (string) $this->login;
    }

    public function getRoles(): array
    {
        // 返回用户角色,例如 ['ROLE_USER']
        return ['ROLE_USER'];
    }

    public function getPassword(): ?string
    {
        return $this->password;
    }

    public function eraseCredentials(): void
    {
        // 可选:清除敏感数据,如明文密码(如果有存储)
    }
}

3. 清理缓存

执行命令清除缓存,确保配置生效:

php bin/console cache:clear

验证

  • 重新测试登录流程,此时密码验证会调用CustomVerySecureHasher::verify()方法
  • 再次执行php bin/console debug:config security,确认App\Entity\Useraccount的哈希配置已正确使用自定义类的ID

内容的提问来源于stack exchange,提问作者Spinogl

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.29 04:32:06