Symfony2 разделяет пользователей в нескольких приложениях - PullRequest
3 голосов
/ 12 марта 2012

У меня есть несколько приложений symfony2, которые используют общие сущности, но используют разные настройки базы данных. Каждая из этих баз данных имеет таблицы user, user_role и role.

В этом и заключается подвох: я хотел бы, чтобы этот пользователь мог войти в систему app1, посетив www.myproject.com/app1/login и после изменения URL-адреса на /app2/ использовать существующий токен ТОЛЬКО , если идентичный пользователь существует в База данных app2 (то же имя пользователя, пароль и соль). В настоящее время он проверяет только то же имя пользователя, которое, согласитесь, весьма неудобно ...

Я не могу понять, когда refreshUser() вызывается ...: - /

Все приложения используют одинаковые User и Role сущности и UserRepository.

Любая помощь будет принята с благодарностью!

UserRepository:

class UserRepository extends EntityRepository implements \Symfony\Component\Security\Core\User\UserProviderInterface{
    /** @var User */
    private $user;

    public function loadUserByUsername($username) {
        /** @var $Q \Doctrine\ORM\Query */
        $Q = $this->getEntityManager()
        ->createQuery('SELECT u FROM CommonsBundle:User u WHERE u.username = :username')
        ->setParameters(array(
            'username' => $username
        ));
        $user = $Q->getOneOrNullResult();
        if ( $user == null ){
            throw new UsernameNotFoundException("");
        }
        return $this->user = $user;
    }

    public function refreshUser(UserInterface $user) {
        return $this->loadUserByUsername($user->getUsername());
    }

    public function supportsClass($class) {
        return $class === 'CommonsBundle\Entity\User';
    }

    public function findById($id){
        return $this->getEntityManager()
            ->createQuery('SELECT u FROM CommonsBundle:User u WHERE u.id = :id')
            ->setParameters(array(
            'id' => $id
            ))
            ->getOneOrNullResult();
    }
}

Пользователь # равно (пользовательский интерфейс):

Я знаю, что есть более красивый способ написать этот метод, но я перепишу его после того, как посмотрим, как это работает:)

public function equals(UserInterface $user)
{
    if (!$user instanceof User) {
          return false;
    }
    if ($this->password !== $user->getPassword()) {
          return false;
    }

    if ($this->getSalt() !== $user->getSalt()) {
          return false;
    }

    if ($this->username !== $user->getUsername()) {
          return false;
    }

    return true;

}

1 Ответ

5 голосов
/ 19 марта 2012

Ваш вопрос заставил меня задуматься. При использовании безопасности symfony2 у вас возникла одна проблема: либо сеанс действителен, то есть пользователь аутентифицирован как анонимный или реальный пользователь, либо сеанс недействителен.

Итак, учитывая это, я не вижу, чтобы ваш подход работал так, как вам хотелось бы, потому что, скажем, user1 входит в систему и использует app1. Теперь он переключается на app2 и не находится в базе данных, что означает, что у него не должно быть доступа. Что делать сейчас? Отменить сессию? Это будет означать, что он должен снова войти в app1.

Если бы вы использовали субдомены, вы могли бы привязать сеанс к этому субдомену, но это означало бы, что пользователь должен снова войти в систему для каждого приложения.

Существует еще одна проблема: похоже, что symfony2 сохраняет идентификатор пользователя в сеансе, поэтому без доступа к базе данных app1 вы не сможете узнать пароль и роли пользователя в базе данных app1 и не сможете проверить за это.

Полагаю, безопасность symfony2 просто не была создана для такого поведения. Ожидается, что сеанс будет относиться к одному и тому же пользователю во всем приложении.

Я не думаю, что symfony2 - большая проблема здесь , но общая обработка с php. Давайте на минуту подумаем, что бы я предложил без symfony2:

Когда пользователь входит в систему, сохраните пользователя и роли в определенном массиве в сеансе, например:

user.app1 = array('username','password',array('role1','role2'))

Теперь при каждом запросе к app1 я бы проверял, присутствует ли user.app1 в сеансе, и считывал оттуда роли. Если нет, я бы проверил user.app2, user.app3 и так далее. Если я не нахожу ничего, перенаправьте на логин. Если я найду один, я бы запросил базу данных, чтобы найти пользователя с тем же именем пользователя и сравнить другие значения. Если совпадают, сохраните все в базу данных. Если нет, проверьте следующего пользователя из сеанса.

Я посмотрел справочник по безопасности Symfony , и у вас есть несколько точек расширения, так что, возможно, вы сможете работать с этого момента. form_login получил success_handler, поэтому добавление массива в сессию, как предложено выше, должно быть сделано там. Сам брандмауэр имеет некоторые параметры, такие как request_matcher и entry_point, которые можно использовать для добавления дополнительных проверок, подобных тем, которые я упоминал выше. Все они определены как сервисы, поэтому внедрение менеджера сущностей и контекста безопасности не должно быть проблемой.

Лично я считаю, что сам дизайн здесь неоптимален, и вам лучше реорганизовать код, чтобы использовать одного пользователя для всех приложений и разных ролей (помните, что вы можете определить много менеджеров сущностей и использовать разные базы данных) или даже консолидировать все базы данных и хранение всего в одной базе данных, используя acl, чтобы пользователи не могли просматривать «неправильный» контент.

...