Symfony 2.1中功能测试的身份验证

19

我目前正在将一个2.0.*项目迁移到当前的Symfony 2.1 beta。

在我的功能性测试中,我目前有这段代码来创建一个带有身份验证的客户端:

$client = // create a normal test client
$role = 'ROLE_USER';
$firewallName = 'main';
$user = // pull a user from db

$client->getCookieJar()->set(new \Symfony\Component\BrowserKit\Cookie(session_name(), true));

$token = new UsernamePasswordToken($user, null, $firewallName, array($role));

self::$kernel->getContainer()->get('session')->set('_security_' . $firewallName, 
serialize($token));

这在2.0.*版本中能够正常工作,但在2.1版本中不能,数据无法设置到会话中。

有什么想法吗?

编辑(添加更多信息):

看起来问题出在文件 "Symfony\Component\Security\Http\Firewall\ContextListener" 的 "onKernelResponse" 方法中。代码如下:

if ((null === $token = $this->context->getToken()) || ($token instanceof AnonymousToken)) {
    $session->remove('_security_'.$this->contextKey);
} else {
    $session->set('_security_'.$this->contextKey, serialize($token));
}

在我的情况下,如果"$token instanceof AnonymousToken"为真,则会删除会话密钥。如果我注释掉那段代码,一切都按预期工作。

因此,我的新问题是:我该怎么做才能使令牌不是匿名的?


我正在使用与你非常相似的代码片段来登录用户进行特定的功能测试。我也在升级到2.1版本,如果能让这个工作起来就太棒了,因为我的很多功能测试现在都失败了。 - josef.van.niekerk
我看到在ContextListener.php中,调用了$this->context->setToken(null),这是因为$session === null而被调用的吗?嗯... - josef.van.niekerk
1
当我注释掉ContextListener.php的第77行 - $this->context->setToken(null)时,我的测试完美地工作了!所以这可能是一个线索! - josef.van.niekerk
5个回答

11

正确的用户认证方式是:

$firewallName = 'your-firewall-name';
$container = self::$kernel->getContainer()
$token = new UsernamePasswordToken($user, null, $firewallName, $user->getRoles());
$container->get('security.context')->setToken($token);

和防火墙认证:

$session = $container->get('session');
$session->set('_security_'.$firewallName, serialize($token));
$session->save();

经过一番搜索,我得出了相同的结论。但是在我的测试中这并不起作用。无论如何,感谢你的回答。 - smoove
你是否也删除了以下这行代码来启动新会话:$client->getCookieJar()->set(new \Symfony\Component\BrowserKit\Cookie(session_name(), true)); - Maciej Pyszyński
我尝试了加和不加那行代码,但结果都没有改变。 - smoove
这对我有用(非常感谢,这已经困扰我很久了)。但是我还必须在security.yml中禁用防火墙: firewalls: test: pattern:^ /。* security:false我想从技术上讲,最好在不禁用防火墙的情况下进行测试,但是仅禁用防火墙也不起作用,因为我需要在系统中拥有经过身份验证的用户(就像您的代码一样)。 无论哪种方式都足够好,我终于可以编写一些登录页面的功能测试了! - timhc22

1
我可以提供另一种解决方案(对我有效)。我有一个自定义的身份验证提供程序,具有复杂的逻辑,无法使用http_basic机制。
您需要创建特殊的操作,如下所示。
 //TestAuthController.php
 public function authTestAction(Request $request)
 { 
   // you can add different security checks as you wish
   $user_id = $request->query->get("id");
   // find user  by $user_id using service or user provider service from firewall config
   $token = new UsernamePasswordToken($user, null, $firewallName, array($role));
    // or another authenticated token
   $this->container->get('security.context')->setToken($token);
 }

添加 routing_test.yml 文件,并像 routing_dev.yml 一样插入它。

重要的是,这个身份验证路由必须仅存在于测试环境中,出于安全原因。

 //app/config/routing_test.yml
 // ... the same routes as in routing_dev.yml
_testauth:
    pattern:  /__test
    defaults: { _controller: MyBundle:TestAuth:authTest }

然后在测试中只需将客户端发送到此URL

public function testSomething()
{
    $client = static::createClient();
    $client->request('GET','/__test',array('id'=>146));
    $this->assertTrue($client->getContainer()->get('security.context')->isGranted('ROLE_USER')) //it will pass;
}

这对我有效,而其他方法没有(使用Sf2.3) - caponica

1

1
在我的测试套件中(正在使用2.0和现在的2.1版本),我使用一个基类WebTestCase,它扩展了Symfony2 WebTestCase类。
我有这两个函数,在我的测试中创建匿名客户端或已登录的客户端:
static protected function createClient(array $options = array(), array $server = array())
{
    $client = parent::createClient($options, $server);

    self::$container = self::$kernel->getContainer();

    return $client;
}

protected function createAuthClient($user, $pass)
{
    return self::createClient(array(), array(
        'PHP_AUTH_USER' => $user,
        'PHP_AUTH_PW'   => $pass,
    ));
}

接下来,在我的测试类中,我使用:

    $client = static::createClient();

创建匿名客户端并

    $client = static::createAuthClient('user','pass');

创建一个经过身份验证的。

这在2.1上运行得非常好。


1
我曾经遇到过同样的问题,在调试时找到了解决方案。根据您的会话存储方式,您可能需要将cookie从session_name()更改为“MOCKSESSID”(如果您使用模拟会话,则会使用该名称)。我认为,如果您在config_test.yml中有以下内容,则会自动更改为模拟会话存储:
framework:
   test: ~

为了完整起见,这是我的全部代码(我正在使用FOS/UserBundle)

    protected function createAuthorizedClient($username = 'user') {
      $client = $this->createClient(); //Normal WebTestCase client     
      $userProvider = $this->get('fos_user.user_manager');
      $user = $userProvider->loadUserByUsername($username);
      //$client->getCookieJar()->set(new Cookie(session_name(), true));
      $client->getCookieJar()->set(new Cookie('MOCKSESSID', true));
      $session = self::$kernel->getContainer()->get('session');
      $token = new UsernamePasswordToken($user, null, 'main', $user->getRoles());
      $client->getContainer()->get('security.context')->setToken($token);
      $session->set('_security_main', serialize($token));

    return $client;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接