【问题标题】:How can you make a Symfony user provider execute a method on an EntityRepository?如何让 Symfony 用户提供者在 EntityRepository 上执行方法?
【发布时间】:2014-09-03 14:50:13
【问题描述】:

我有一个 Symfony 2 应用程序,它使用 Cosign 单点登录解决方案进行身份验证 (https://github.com/fmfi-svt/cosign-bundle),然后使用自定义用户提供程序来设置角色并处理任何特定路由上的授权。角色的设置主要基于不同 LDAP 组中的成员身份,但我还需要查看用户在数据库中的状态是否为“已批准”。 LDAP 部分正在工作,但我不知道如何允许用户提供程序为我设置为服务的实体调用 EntityRepository 方法。基本上,从我的用户提供者内部,我希望能够像这样使用 Person 实体存储库:

$status = $personRepository->findStatusByUsername();

我假设我需要使用依赖注入来使 EntityRepository 可用于用户提供程序,但我似乎无法弄清楚如何做到这一点。问题似乎归结为 User Provider 没有实例化为对象,所以我不能使用 $this。我最近的尝试,如下代码所示,使用了属性类型依赖注入的方法,但是当我尝试在 is_approved() 方法中使用时,Symfony 仍然认为常量 $personRepository 是未定义的。

错误:C:\xampp55\htdocs\symtran2\src\Ginsberg\TransportationBundle\Security\User\UserProvider.php 第 220 行中未定义的类常量 'personRepository'

作为背景,登录用户和 Person 实体在应用程序中是有区别的。最常见的情况是登录用户是管理 Persons 的管理员,尽管 Person 也可以登录并管理自己的信息(例如,在系统中为自己进行预订)。登录用户的身份由 Cosign 提供,这使得用户的用户名在 $_SERVER['REMOTE_USER'] 中可用。因此,您始终可以通过检查该值来判断谁已登录。这样做的结果是不需要“用户”表来跟踪用户名。但是,有两个与用户信息相关的类:

  • User 类实现了 UserInterface 和 EquatableInterface 和 似乎是 Symfony 安全系统用来 管理授权。 (我通过遵循 Cookbook 中有关创建自定义用户提供程序的说明: http://symfony.com/doc/current/cookbook/security/custom_provider.html.)

  • 上面提到的 Person 实体,它跟踪有关 系统中用户的状态,例如给定 Person 所处的阶段 在审批过程中。

我已经尝试将 PersonRepository 变成一个服务,然后将该服务提供给 User Provider 服务,如下所示:

parameters:
ginsberg_transportation.user.class: Ginsberg\TransportationBundle\Services\User
user_provider.class: Ginsberg\TransportationBundle\Security\User\UserProvider

services:
  ginsberg_user:
    class: "%ginsberg_transportation.user.class%"
 user_provider:
    class: "%user_provider.class%"
    properties:
      personRepository: "@ginsberg_person.person_repository"
  ginsberg_transportation.form.type.person:
    class: Ginsberg\TransportationBundle\Form\Type\PersonType
    tags:
      - { name: form.type, alias: person }
  ginsberg_person.person_repository:
    class: Doctrine\ORM\EntityRepository
    factory_service: doctrine.orm.default_entity_manager
    factory_method: getRepository
    arguments:
      - Ginsberg\TransportationBundle\Entity\Person

UserProvider 类很长,但相关部分是:

namespace Ginsberg\TransportationBundle\Security\User;

use Symfony\Component\Security\Core\User\UserProviderInterface;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Core\Exception\UsernameNotFoundException;
use Symfony\Component\Security\Core\Exception\UnsupportedUserException;
use Ginsberg\TransportationBundle\Entity\Person;
use Doctrine\ORM\EntityRepository;


class UserProvider implements UserProviderInterface
{
  public $personRepository;

   protected static $_host = 'ldap.itd.umich.edu';
    // The umbrella group that lists the subgroups of eligible drivers
    protected static $_eligible_group = 'ginsberg transpo eligible';
    protected static $_admin_group = 'ginsberg transportation admins';
    protected static $_superuser_group = 'ginsberg transportation superusers';
    protected static $_pts_group = 'ginsberg pts staff';
    public static $_pts_group_email = 'ginsberg-pts-staff@umich.edu';

    public function loadUserByUsername($uniqname)
    {
      $password = "admin";
      $salt = "";
      $roles = array();

      if (self::is_authenticated()) {
        if (self::is_superuser() && self::is_approved()) {
          $roles[] = 'ROLE_SUPER_ADMIN';
        } elseif (self::is_admin() && self::is_approved()) {
          $roles[] = 'ROLE_ADMIN';
        } elseif (self::is_eligible() && self::is_approved()) {
          $roles[] = 'ROLE_USER';
        }

        return new User($uniqname, $password, $salt, $roles);
      }

      throw new UsernameNotFoundException(
      sprintf('Username "%s" does not exist.', $uniqname));
    }

    public function refreshUser(UserInterface $user) {
      if (!$user instanceof User) {
        throw new UnsupportedUserException(
          sprintf('Instances of "%s" are not supported.', get_class($user))
        );
      }

      return $this->loadUserByUsername($user->getUsername());
    }

    public function supportsClass($class) {
      return $class === 'Ginsberg\TransportationBundle\Security\User\User.php';
    }

    /**
     * Gets uniqname based on value of $_SERVER['REMOTE_USER'] or supply hard-coded value for testing
     *
     * @return string User's uniqname or false if not found
     */
    public static function get_uniqname()
    {
      // If we are in a cosign environment, return the user uniqname from
      // REMOTE_USER
      if (isset( $_SERVER['REMOTE_USER'] ) && !empty( $_SERVER['REMOTE_USER'] )) {
        return $_SERVER['REMOTE_USER'];
      }

      // for local debug:
      if(!isset( $_SERVER['REMOTE_USER'] ) &&
          $_SERVER[ 'SERVER_NAME' ] === 'localhost') {
        return 'ericaack';
          }

    return false;
    }


    /**
     * Check whether user is logged in through Cosign.
     *
     * @return boolean Whether or not the user is authenticated
     */
    public static function is_authenticated()
    {
      if (self::get_uniqname() != False) {
        return True;
      }
      return False;
    }

    /**
     * Checks whether or not user is approved in Ginsberg transpo database
     *
     * @return boolean Whether user is approved
     */
    public static function is_approved()
    {
      $uniqname = self::get_uniqname();
      $personRep = self::personRepository;
      $status = $personRep->findStatusByUniqname($uniqname);

      return($status == 'approved') ? TRUE : FALSE;

    }

任何帮助将不胜感激。

【问题讨论】:

  • 长问题! UserProvider 应该是一项服务,这意味着您的存储库可以很容易地被注入。 “用户提供者没有被实例化为对象”没有意义。
  • @Cerad,谢谢,很抱歉给了我这么长的时间。我以为我已经从 services.yml 文件中将用户提供者变成了上面显示的设置中的服务——我在那里犯了错误吗?将 User Provider 正确配置为服务后,我应该如何访问 User Provider 中的存储库?
  • @Cerad,我试图用“UserProvider 未实例化为对象”说的是,当我使用 $this 时,我收到“不在对象上下文中使用 $this”错误.在控制器中,您可以执行 $personRepository = $this->get('ginsberg_person.person_repository'); $status = $personRepository->findStatusByUsername();但这在用户提供程序中不起作用。

标签: php symfony


【解决方案1】:

我能够通过在 UserProvider 类中使用注入来实现这一点(Symfony 3)。我在类中设置了一个类变量和setter方法。此外,我必须在我的 services.yml 文件中设置服务。我会在这里向你展示这两个。

UserProvider 类

use Doctrine\ORM\EntityRepository;
//make sure to include all other relevant uses!!

class WebserviceUserProvider implements UserProviderInterface{
private $er; //**HERE IS WHERE WE STORE THE EntityRepository**

/**
 * {@inheritDoc}
 * @see \Symfony\Component\Security\Core\User\UserProviderInterface::loadUserByUsername()
 */
public function loadUserByUsername($username)
{
    if ($username != '') {
        //WE CAN USE THE EntityRepository here!
        $user = $this->er->loadUserByUsername($username);
        if (!empty($user)){
            //Call the UserService or whatever you need to do
        }           
    }

    throw new UsernameNotFoundException(
            sprintf('Username "%s" does not exist.', $username)
            );
}

    /**
     * Injection method to allow us to use the EntityRepository for User
     * here. Otherwise we don't have access to it.
     * @param EntityRepository $em
    */
    public function setEr(EntityRepository $er){
        $this->er = $er;
    }

    //Include any other methods here you need for your UserProvider Class
    //I've left them out for brevity
}

services.yml 您必须创建存储库服务并将其链接到您需要使用它的 UserProvider!

services:
    my_repository:
        class: Doctrine\ORM\EntityRepository
        factory: ['@doctrine.orm.default_entity_manager', getRepository]
        arguments:
            - AcmeBundle\Entity\User
    app.webservice_user_provider:
        class: AcmeBundle\Security\User\WebserviceUserProvider
        calls:
             - [setEr, ['@my_repository']]

【讨论】:

    猜你喜欢
    • 2017-04-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多