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

Symfony 6.2配置MD5密码哈希验证失败,寻求解决方案

问题描述

我正在将一个重型Java应用迁移至Symfony 6.2开发的Web应用,要求完全沿用现有数据库,该数据库中密码采用MD5哈希存储。我已能通过hash('md5', $password)实现MD5哈希,但希望像Symfony默认方式一样自动完成哈希验证。

我按照Symfony文档尝试在security.yaml中添加如下配置:

password_hashers:
app_hasher:
id: 'App\Security\Hasher\CustomVerySecureHasher'

并创建了对应的CustomVerySecureHasher类:

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

        // Hash the password using the MD5 algorithm
        return md5($plainPassword);
    }

    public function verify(string $hashedPassword, string $plainPassword): bool
    {
        // Compare the hashed password with the MD5 hashed plaintext password
        return $hashedPassword === md5($plainPassword);
    }

    public function needsRehash(string $hashedPassword): bool
    {
        // There is no need to rehash with MD5, as it is considered insecure
        return false;
    }
}

但该配置并未生效。我已确认通过表单能正确获取登录账号和密码,问题出在哈希验证环节。以下是我的相关代码及配置:

AppCustomAuthenticator类

class AppCustomAuthenticator 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('login', '');

        $request->getSession()->set(Security::LAST_USERNAME, $email);

//        $password = $request->request->get('password', '');
//        $test = hash('md5', $password);
//        dd($test);

        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);
    }
}

登录控制器代码

#[Route(path: '/', name: 'app_login')]
public function login(AuthenticationUtils $authenticationUtils): Response
{
    // if ($this->getUser()) {
    //     return $this->redirectToRoute('target_path');
    // }

    // get the login error if there is one
    $error = $authenticationUtils->getLastAuthenticationError();
    // last username entered by the user
    $lastUsername = $authenticationUtils->getLastUsername();
//        if ($lastUsername) {
//            dd($lastUsername);
//        }

    return $this->render('/security/login.html.twig', ['login' => $lastUsername, 'error' => $error]);
}

完整的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

使用环境:PHP 8.1、Symfony 6.2


解决思路

1. 替换用户提供者配置

当前使用的users_in_memory内存提供者不会从数据库加载用户,无法匹配数据库中的MD5密码。需要替换为数据库实体提供者:

修改security.yaml的providers和main防火墙配置:

providers:
    app_user_provider:
        entity:
            class: App\Entity\Useraccount
            property: email # 替换为Useraccount实体中存储登录账号的字段(如username,需和表单提交的login参数对应)
firewalls:
    main:
        lazy: true
        provider: app_user_provider # 替换原有的users_in_memory
        custom_authenticator: App\Security\AppCustomAuthenticator

2. 完善User实体接口实现

App\Entity\Useraccount必须实现PasswordAuthenticatedUserInterface和UserInterface,并实现必要方法:

namespace App\Entity;

use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface;
use Symfony\Component\Security\Core\User\UserInterface;

class Useraccount implements UserInterface, PasswordAuthenticatedUserInterface
{
    // 实体原有字段和方法

    public function getPassword(): string
    {
        return $this->password; // 返回数据库中存储MD5哈希的字段
    }

    public function getRoles(): array
    {
        // 根据业务逻辑返回角色,默认可返回['ROLE_USER']
        return ['ROLE_USER'];
    }

    public function eraseCredentials()
    {
        // 无需处理,留空即可
    }

    public function getUserIdentifier(): string
    {
        return $this->email; // 和提供者配置的property字段一致,返回用户唯一标识
    }
}

3. 确保自定义哈希器被容器识别

如果Symfony未自动注册CustomVerySecureHasher,需在services.yaml中手动配置:

services:
    App\Security\Hasher\CustomVerySecureHasher:
        autowire: true
        autoconfigure: true

4. 验证配置并清除缓存

完成所有修改后,清除Symfony缓存:

php bin/console cache:clear

此时登录流程会自动调用自定义哈希器的verify方法,对比提交密码的MD5哈希与数据库存储值。


内容的提问来源于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 21:25:10