Symfony 5.1.2引入链式用户提供者后UserProviderInterface服务丢失问题
这个问题我之前也碰到过,核心原因是Symfony在用户提供者数量变化后的服务绑定逻辑发生了变化:
为什么之前正常,现在报错?
当你只有一个用户提供者(比如之前的backend_users)时,Symfony会自动把UserProviderInterface这个接口别名到这个唯一的提供者服务上,所以你通过UserProviderInterface::class能拿到对应的实例。但当你配置了多个提供者并启用链式提供者后,容器里存在多个实现了UserProviderInterface的服务(memory提供者、entity提供者、链式提供者本身),Symfony无法自动判断该把接口绑定到哪个,所以就不再自动创建这个别名了——这就是你看到You have requested a non-existent service...错误的原因。
两种快速修复方法
方法1:手动绑定接口到链式提供者
在你的config/services.yaml中添加一行,明确告诉容器UserProviderInterface应该指向你定义的链式提供者:
services: # 把UserProviderInterface绑定到链式提供者的服务 Symfony\Component\Security\Core\User\UserProviderInterface: '@security.user.provider.concrete.all_users'
这里的security.user.provider.concrete.all_users是Symfony为你配置的all_users链式提供者自动生成的服务ID,格式固定为security.user.provider.concrete.{你的提供者名称}。
方法2:在测试中直接获取链式提供者服务
修改你基类里的connectUser方法,直接获取链式提供者的服务ID,而不是通过接口:
public function connectUser(string $username) { // 直接链式提供者的服务ID $userProvider = static::$container->get('security.user.provider.concrete.all_users'); $user = $userProvider->loadUserByUsername($username); $this->assertNotNull($user); $this->kernelBrowser->loginUser($user); }
这种方法不需要修改全局服务配置,只针对测试代码调整,适合不想影响其他业务逻辑的场景。
额外说明
ChainUserProvider确实是实现了UserProviderInterface的,所以不管用哪种方法,你后续调用loadUserByUsername的逻辑完全不用改,只是获取服务的方式变了而已。
内容的提问来源于stack exchange,提问作者Juste

