可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I am currently in the process of migrating a 2.0.* project to the current 2.1 beta of Symfony.
In my functional tests i currently have this code to create a client with authentication:
$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));
this works as expected in 2.0.* but not in 2.1, the data does not get set in the session.
Any ideas?
Edit (adding more info):
it seems that the problem lies in the file "Symfony\Component\Security\Http\Firewall\ContextListener" in the method "onKernelResponse". There is this code:
if ((null === $token = $this->context->getToken()) || ($token instanceof AnonymousToken)) {
$session->remove('_security_'.$this->contextKey);
} else {
$session->set('_security_'.$this->contextKey, serialize($token));
}
in my case the if "$token instanceof AnonymousToken" is true, and because of that the session key gets removed. if i comment out that code everything works as expected.
So i guess my new question is: What can i do to make the token not anonymous?
回答1:
Proper way to authenticate user is:
$firewallName = 'your-firewall-name';
$container = self::$kernel->getContainer()
$token = new UsernamePasswordToken($user, null, $firewallName, $user->getRoles());
$container->get('security.context')->setToken($token);
and firewall authentication:
$session = $container->get('session');
$session->set('_security_'.$firewallName, serialize($token));
$session->save();
回答2:
I've logged a ticket on: https://github.com/symfony/symfony/issues/3287
There appears to be an existing issue with Symfony 2.1 RC.
回答3:
In my testsuite (working on 2.0 and now 2.1) I use a Base class WebTestCase that extends the Symfony2 WebTestCase class.
I have these two functions that create an anonymous client or a logged one in my tests:
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,
));
}
Then, in my Test classes, I use:
$client = static::createClient();
to create an anon client and
$client = static::createAuthClient('user','pass');
to create an authenticated one.
This works like a charm on 2.1
回答4:
I can suggest another solution (thus it working for me). I have an custom authentication provider, with complicated logic and I can't use http_basic mechanic at all.
You need to create special Action like this
//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);
}
add routing_test.yml and plug it just as routing_dev.yml
Important thing that this authentication route must exists only in test enviroment, for security reasons.
//app/config/routing_test.yml
// ... the same routes as in routing_dev.yml
_testauth:
pattern: /__test
defaults: { _controller: MyBundle:TestAuth:authTest }
Then in test just send client to this 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;
}
回答5:
i had the same issue and found a solution while debugging. Depending on your Session Storage, you might have to change your cookie from session_name() to 'MOCKSESSID' (which is used if your using the mock session. I think if you have the following in your config_test.yml it is automatically changed to Mock Session Storage:
framework:
test: ~
For completeness, here my full code (i'm using 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;
}