Symfony 身份验证提供程序

Posted

技术标签:

【中文标题】Symfony 身份验证提供程序【英文标题】:Symfony authentication providers 【发布时间】:2015-08-24 07:37:46 【问题描述】:

我正在使用fr3d/ldap-bundle。如果他们不在数据库中,它会让我登录并从 AD 导入用户。没关系。

尽管有 AD 用户,但我也有本地用户,它们在我的数据库中。有一个特殊的列 authType 说明了用户应该如何被认证——通过 LDAP 或本机( FOS )。我已经创建了自己的用户提供程序:

public function chooseProviderForUsername($username)

    if($user->getAuthType() == User::LOGIN_LDAP) 
         $this->properProvider = $this->ldapUserProvider;
      elseif($user->getAuthType() == User::LOGIN_NATIVE) 
         $this->properProvider = $this->fosUserProvider;
      else 
         throw new InvalidArgumentException('Error');
     


public function loadUserByUsername($username)

    return $this->chooseProviderForUsername($username)->loadUserByUsername($username);

问题:链供应商不是一个选项 - 它允许用户使用他的 LDAP 密码和他的本地密码登录!这是一个很大的安全问题。

是否有办法通过不同的身份验证提供程序登录用户,具体取决于 db 字段?

编辑:

我的 security.yml:

 providers:
        fos_userbundle:
            id: fos_user.user_provider.username
        appbundle_user_provider:
            id: appbundle.user_provider
        fr3d_ldapbundle:
            id: fr3d_ldap.security.user.provider

    firewalls:
        dev:
            pattern: ^/(_(profiler|wdt)|css|images|js)/
            security: false
        admin:
            pattern: ^/admin.*
            context: user
            fr3d_ldap:  ~
            form_login:
                provider: appbundle_user_provider
                csrf_provider: security.csrf.token_manager
                always_use_default_target_path: true
                default_target_path: admin_main
                login_path: /admin/login
                check_path: /admin/login_check
            logout:
                path:   /admin/logout
                target: /admin/login
            anonymous:    true

这里是security.yml。 fr3d_ldap: ~ 这一行启用了 ldap 包,它授权 ldap 用户并将他们保存到我的数据库中。没有它我无法授权他们,可能我必须编写自定义 AuthenticationProvider。

【问题讨论】:

你能解释一下“链提供者”吗? symfony.com/doc/current/cookbook/security/… 我们能否查看您的 security.yml 文档,其中指定了提供者? 现在检查 - 我已经添加了。 【参考方案1】:

我能想到的最简洁的方法是创建自己的ChainProvider class,它只允许使用一个提供商和use the Dependency Injection Container to use yours 登录。

您只需要覆盖您的包配置文件中的 security.user.provider.chain.clas 参数定义。

【讨论】:

我做过类似的事情,但最终防火墙中的fr3d_ldap: ~ 正在确保检查 ldap 服务器的安全性,无论我的提供商是什么。如果没有这条线,我将无法完全验证“动态”创建的用户。我必须检查@Joe Yahchouchi 的答案。【参考方案2】:

好的,非常简短的回答,但我认为目前 Symfony 正在任何旧用户提供程序中搜索用户,而不是您希望为特定用户搜索的用户(这解释了使用两个密码登录的整个过程)。解决方案应该是让AppBundleUserProvider 实现UserProviderInterface,从security.yml 中删除其他用户提供程序,然后确保AppBundleUserProvider 的第一件事是找出该用户需要哪个用户提供程序,然后模仿它对于UserProviderInterface 中的每个方法。您可以根据用户名设置$this->realUP,然后将每个方法设置为只返回$this->realUP->someMethod()

【讨论】:

是的,这是解决问题的一种方法,您可以看到我已经尝试过...我正在使用 appbundle_user_provider。虽然它没有实现 UserProviderInterface,但我认为变化不大。 您是否尝试过先使用此提供商的链路由,然后再使用 ldap 的?目前 symfony 只是使用任何用户提供者,它返回与提供的凭据匹配的用户对象。 是的,我做到了,但它会产生使用两个密码登录的问题 - 如果第一个提供者没有通过身份验证,那么第二个提供者会。 嗯,这是一个头条新闻。我过去做过类似的事情,您是否愿意将整个应用程序包用户提供程序发布或发送给我以供早上查看? 用户提供者只查找用户,不多。 ldap 用户提供者,如果它在 ldap 服务器上找到用户,它会在本地数据库中创建用户实体并保存它。 FOS 只需检查本地数据库。而且我只想让 LDAP 检查这个 authType 字段并基于该授权用户。【参考方案3】:

您的方法看起来不错,但您应该检查方法的逻辑。 首先是这个:

public function chooseProviderForUsername($username)

    if($user->getAuthType() == User::LOGIN_LDAP) 
         $this->properProvider = $this->ldapUserProvider;
      elseif($user->getAuthType() == User::LOGIN_NATIVE) 
         $this->properProvider = $this->fosUserProvider;
      else 
         throw new InvalidArgumentException('Error');
     

您将$username 作为参数传递给此方法,然后使用$user 对象,该对象在当前上下文中似乎未定义。 其次:

public function loadUserByUsername($username)

    return $this->chooseProviderForUsername($username)->loadUserByUsername($username);

所以chooseProviderForUsername 方法实际上不返回任何值,你不能以这种方式链接它。

我希望重构这些问题能让您的提供程序正常工作。

【讨论】:

啊,该死,这只是我的代码的摘要,并没有涵盖所有内容。它需要用户然后对其进行操作。问题是它总是使用这两个提供者.. 当你说它需要用户时,它是如何操纵它的?该方法是否采用用户名然后使用它来查找用户对象?如果是这样,它是如何做到的?【参考方案4】:

我对 ldap 不是很熟悉,但我建议尝试完全手动登录

  $token = new UsernamePasswordToken($user, null, "firewallname", $user->getRoles());
  $securityContext = $this->container->get('security.context');
  $securityContext->setToken($token);

然后您可以自己手动进行检查,并根据检查结果决定在验证之前如何验证用户。例如,在执行此登录代码或其他任何内容之前通过用户名和密码运行查询,具体取决于您想要的 db 字段。

【讨论】:

是的,但问题是当用户不在数据库中时怎么办?然后它应该从 ldap 提供者那里获取(就像现在一样),将它存储在 db 中(就像现在一样)并授权他(以及 - 现在是这样)。 从 ldap 信息构建用户实体并验证该用户。我认为这应该有效。我认为以后是否存储该用户实体是可选的。 好吧,我认为这不是可选的。至少我找不到任何选项是否保存实体。并且用户实体正在由我当前使用的包构建。

以上是关于Symfony 身份验证提供程序的主要内容,如果未能解决你的问题,请参考以下文章

Symfony 身份验证提供程序

Symfony2 - 安装了 FOS 用户包的自定义身份验证提供程序

在 Symfony2 中使用自定义身份验证提供程序

Symfony2 实体用户提供者覆盖自定义身份验证提供者

Symfony2 通过第 3 方 REST API 进行身份验证

从 android/ios 使用 Symfony 进行身份验证