我有多个共享公共实体的symfony2应用程序,但使用不同的数据库设置。每个数据库都有表user
,user_role
和role
。
以下是问题:我希望该用户能够通过访问app1
并在将网址更改为www.myproject.com/app1/login
后使用现有令牌来登录/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();
}
}
用户#等于(的UserInterface):
我知道有一种比较漂亮的方法来编写这个方法,但我会在看到这个工作后重写它:)
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;
}
答案 0 :(得分:5)
您的问题让我思考。使用symfony2安全性时,您遇到一个问题:会话有效,意味着用户被认证为匿名用户或真实用户,或者会话无效。
所以,考虑到这一点,我没有看到你的方法按照你的意愿工作,因为让我们说user1登录并使用app1。现在他切换到app2并且不在数据库中,这意味着他不应该访问。现在做什么?使会话无效?这意味着他必须再次登录app1。
如果您要使用子域,则可以将会话绑定到该子域,但这意味着用户必须再次为每个应用程序登录。
还有另一个问题:似乎symfony2将用户的id存储到会话中,因此无法访问app1数据库,您无法知道app1数据库中用户的密码和角色是什么,无法检查为了它。
我猜symfony2的安全性根本就不是针对这种行为的。它希望会话与整个应用程序中的同一用户相关。
我认为symfony2不是这里的大问题,而是用php进行整体处理。让我们思考一下如果没有symfony2我会建议:
当用户登录时,将用户和角色存储到会话中的特定阵列中,例如:
user.app1 = array('username','password',array('role1','role2'))
现在,在每次向app1发出请求时,我会检查user.app1是否在会话中并从那里读取角色。如果没有,我会检查user.app2,user.app3等。如果我找不到,请重定向登录。如果我找到一个,我会查询数据库以找到具有相同用户名的用户并比较其他值。如果匹配,将所有内容存储到数据库中。如果没有,请从会话中检查下一位用户。
我抬起了symfony security reference,你有一些扩展点,所以也许你可以从那里开始工作。 form_login
获得了success_handler
,因此应该在此处将数组添加到会话中。防火墙本身有一些参数,如request_matcher
和entry_point
,可以用来添加我上面提到的其他检查。所有都被定义为服务,因此注入实体管理器和安全上下文应该没问题。
我个人认为设计本身并不是最优的,您可能更好地重构代码,以便为所有应用程序和不同角色使用一个用户(请记住,您可以定义许多实体管理器并使用不同的数据库)甚至整合所有数据库并将所有内容存储到一个数据库中,使用acl防止用户查看“错误”的内容。