Answers:
Symfony 4.0
此过程尚未从symfony 3更改为4,但这是使用新推荐的AbstractController的示例。无论是security.token_storage
和session
服务注册在父getSubscribedServices
方法,所以你不要有添加这些在你的控制器。
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use YourNameSpace\UserBundle\Entity\User;
class LoginController extends AbstractController{
public function registerAction()
{
$user = //Handle getting or creating the user entity likely with a posted form
$token = new UsernamePasswordToken($user, null, 'main', $user->getRoles());
$this->container->get('security.token_storage')->setToken($token);
$this->container->get('session')->set('_security_main', serialize($token));
// The user is now logged in, you can redirect or do whatever.
}
}
Symfony 2.6.x-Symfony 3.0.x
从symfony 2.6开始security.context
,不推荐使用security.token_storage
。控制器现在可以简单地是:
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use YourNameSpace\UserBundle\Entity\User;
class LoginController extends Controller{
public function registerAction()
{
$user = //Handle getting or creating the user entity likely with a posted form
$token = new UsernamePasswordToken($user, null, 'main', $user->getRoles());
$this->get('security.token_storage')->setToken($token);
$this->get('session')->set('_security_main', serialize($token));
}
}
尽管不推荐使用此功能security.context
,但仍可以向后兼容,因此仍可以使用。只需准备为Symfony 3更新它
您可以在此处阅读有关2.6更改的更多信息:https : //github.com/symfony/symfony/blob/2.6/UPGRADE-2.6.md
Symfony 2.3.x版
要在symfony 2.3中实现此目的,您不再可以仅在安全上下文中设置令牌。您还需要将令牌保存到会话中。
假设安全文件带有防火墙,例如:
// app/config/security.yml
security:
firewalls:
main:
//firewall settings here
和控制器动作也类似:
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use YourNameSpace\UserBundle\Entity\User;
class LoginController extends Controller{
public function registerAction()
{
$user = //Handle getting or creating the user entity likely with a posted form
$token = new UsernamePasswordToken($user, null, 'main', $user->getRoles());
$this->get('security.context')->setToken($token);
$this->get('session')->set('_security_main',serialize($token));
//Now you can redirect where ever you need and the user will be logged in
}
}
对于令牌的创建,您将需要创建一个UsernamePasswordToken
,它接受4个参数:用户实体,用户凭据,防火墙名称,用户角色。您无需提供用户凭据即可使令牌有效。
security.context
如果您要立即重定向,我不是100%确定必须在上设置令牌。但这似乎没有伤害,所以我离开了。
然后是重要的部分,设置会话变量。变量命名约定_security_
后跟您的防火墙名称,在这种情况下main
,_security_main
$this->get('session')->set('_security_main', serialize($token));
。谢谢@Chausser!
main
AND 的防火墙设置令牌,那么您将通过另一个名为防火墙的身份验证admin
(因为您要冒充用户),则发生了一件奇怪的事情:_security_admin
获取UsernamePasswordToken
与您提供的用户的,即您与您的admin
防火墙。任何想法如何维护“管理员”防火墙的令牌?
setToken(..)
在同一目标防火墙下调用或未经身份验证时,它才起作用。
终于找到了这个。
用户注册后,您应有权访问在提供程序配置中设置为用户实体的对象实例。解决方案是使用该用户实体创建一个新令牌,并将其传递到安全上下文中。这是一个基于我的设置的示例:
RegistrationController.php:
$token = new UsernamePasswordToken($userEntity, null, 'main', array('ROLE_USER'));
$this->get('security.context')->setToken($token);
main
您的应用程序的防火墙名称在哪里(感谢@Joe)。这就是全部内容。系统现在将您的用户视为刚创建的用户完全登录。
编辑:根据@Miquel的评论,我已经更新了控制器代码示例,以包括适用于新用户的明智默认角色(尽管显然可以根据您的应用程序的特定需求进行调整)。
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
如果您有一个UserInterface对象(大多数情况下应该是这种情况),则可能要使用它为最后一个参数实现的getRoles函数。因此,如果您创建一个函数logUser,它将看起来像这样:
public function logUser(UserInterface $user) {
$token = new UsernamePasswordToken($user, null, 'main', $user->getRoles());
$this->container->get('security.context')->setToken($token);
}
我使用的是Symfony 2.2,我的经验与Problematic的经验略有不同,因此这是该问题的所有信息加上我自己的一些信息的组合版本。
我认为Joe关于构造函数$providerKey
的第三个参数的值是错误的UsernamePasswordToken
。它应该是身份验证(而非用户)提供程序的密钥。身份验证系统使用它来区分为不同提供者创建的令牌。派生自UserAuthenticationProvider
其的任何提供者将仅对提供者密钥与其自身匹配的令牌进行身份验证。例如,UsernamePasswordFormAuthenticationListener
设置它创建的令牌的密钥以匹配其对应的密钥DaoAuthenticationProvider
。这样一来,单个防火墙就可以拥有多个用户名和密码提供程序,而无需彼此踩踏。因此,我们需要选择一个不会与其他任何提供程序冲突的密钥。我用'new_user'
。
在我的应用程序的其他部分中,有一些系统依赖于身份验证成功事件,而仅通过在上下文中设置令牌并不会触发该系统。我必须EventDispatcher
从容器中获取并手动触发该事件。我决定不触发交互式登录事件,因为我们隐式地对用户进行身份验证,而不是响应明确的登录请求。
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
use Symfony\Component\Security\Core\AuthenticationEvents;
use Symfony\Component\Security\Core\Event\AuthenticationEvent;
$user = // get a Symfony user instance somehow
$token = new UsernamePasswordToken(
$user, null, 'new_user', $user->getRoles() );
$this->get( 'security.context' )->setToken( $token );
$this->get( 'event_dispatcher' )->dispatch(
AuthenticationEvents::AUTHENTICATION_SUCCESS,
new AuthenticationEvent( $token ) );
请注意,$this->get( .. )
假设片段是在控制器方法中使用的。如果您在其他地方使用代码,则必须更改代码以ContainerInterface::get( ... )
适合环境的方式进行调用。发生这种情况时,我的用户实体实现了,UserInterface
因此我可以直接将其与令牌一起使用。如果您不这样做,则必须找到一种将它们转换为UserInterface
实例的方法。
该代码有效,但我觉得它是围绕Symfony的身份验证体系结构而不是与其一起工作。用自己的令牌类实现新的身份验证提供程序,而不是劫持令牌,可能会更正确UsernamePasswordToken
。同样,使用适当的提供程序将意味着您已处理了这些事件。
使用Symfony 4.4,您可以在控制器方法中简单地执行以下操作(请参见Symfony文档:https : //symfony.com/doc/current/security/guard_authentication.html#manually-authenticating-a-user ):
// src/Controller/RegistrationController.php
// ...
use App\Security\LoginFormAuthenticator;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Security\Guard\GuardAuthenticatorHandler;
class RegistrationController extends AbstractController
{
public function register(LoginFormAuthenticator $authenticator, GuardAuthenticatorHandler $guardHandler, Request $request)
{
// ...
// after validating the user and saving them to the database
// authenticate the user and use onAuthenticationSuccess on the authenticator
return $guardHandler->authenticateUserAndHandleSuccess(
$user, // the User object you just created
$request,
$authenticator, // authenticator whose onAuthenticationSuccess you want to use
'main' // the name of your firewall in security.yaml
);
}
}
重要的一件事,请确保您的防火墙未设置为lazy
。如果是这样,令牌将永远不会存储在会话中,并且您也永远不会登录。
firewalls:
main:
anonymous: ~ # this and not 'lazy'
就像这里已经提到的“问题”一样,这个难以捉摸的$ providerKey参数实际上只是防火墙规则的名称,在下面的示例中为“ foobar”。
firewalls:
foobar:
pattern: /foo/
blablabla
作为第三个参数传递给UsernamePasswordToken的原因吗?这个参数是什么意思?
我在这里尝试了所有答案,但没有一个有效。我可以在控制器上对用户进行身份验证的唯一方法是发出子请求,然后进行重定向。这是我的代码,我使用的是silex,但您可以轻松地使其适应symfony2:
$subRequest = Request::create($app['url_generator']->generate('login_check'), 'POST', array('_username' => $email, '_password' => $password, $request->cookies->all(), array(), $request->server->all());
$response = $app->handle($subRequest, HttpKernelInterface::MASTER_REQUEST, false);
return $app->redirect($app['url_generator']->generate('curriculos.editar'));
在Symfony版本2.8.11(可能适用于旧版本和较新版本)上,如果使用FOSUserBundle,则只需执行以下操作:
try {
$this->container->get('fos_user.security.login_manager')->loginUser(
$this->container->getParameter('fos_user.firewall_name'), $user, null);
} catch (AccountStatusException $ex) {
// We simply do not authenticate users which do not pass the user
// checker (not enabled, expired, etc.).
}
就像我在其他解决方案中看到的那样,无需调度事件。
从FOS \ UserBundle \ Controller \ RegistrationController :: authenticateUser注入
(来自composer.json FOSUserBundle版本:“ friendsofsymfony / user-bundle”:“〜1.3”)