如何以编程方式登录/验证用户?

我想在注册过程后立即登录用户,而无需通过登录表单。

这可能吗?我已经找到了一个解决方案,但我没有在我实际从事的项目上使用它。FOSUserBundle

这是我的安全.yml,我正在使用两个防火墙。纯文本编码器仅用于测试。

security:
    encoders:
        Symfony\Component\Security\Core\User\User: plaintext
        Ray\CentralBundle\Entity\Client: md5

    role_hierarchy:
        ROLE_ADMIN:       ROLE_USER
        ROLE_SUPER_ADMIN: [ROLE_USER, ROLE_ADMIN, ROLE_ALLOWED_TO_SWITCH]

    providers:
        in_memory:
            users:
                admin: { password: admin, roles: [ 'ROLE_ADMIN' ] }
        entity:
            entity: { class: Ray\CentralBundle\Entity\Client, property: email }

    firewalls:
        dev:
            pattern:  ^/(_(profiler|wdt)|css|images|js)/
            security: false

        user_login:
            pattern:    ^/user/login$
            anonymous:  ~

        admin_login:
            pattern:    ^/admin/login$
            anonymous:  ~

        admin:
            pattern:    ^/admin
            provider: in_memory
            form_login:
                check_path: /admin/login/process
                login_path: /admin/login
                default_target_path: /admin/dashboard
            logout:
                path:   /admin/logout
                target: /

        site:
            pattern:    ^/
            provider: entity
            anonymous:  ~
            form_login:
                check_path: /user/login/process
                login_path: /user/login
                default_target_path: /user
            logout:
                path:   /user/logout
                target: /

    access_control:
        - { path: ^/user/login, roles: IS_AUTHENTICATED_ANONYMOUSLY }
        - { path: ^/admin/login, roles: IS_AUTHENTICATED_ANONYMOUSLY }
        - { path: ^/user, roles: ROLE_USER }
        - { path: ^/admin, roles: ROLE_ADMIN }
        - { path: ^/, roles: IS_AUTHENTICATED_ANONYMOUSLY }

答案 1

是的,您可以通过类似于以下内容的方式执行此操作:

use Symfony\Component\EventDispatcher\EventDispatcher,
    Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken,
    Symfony\Component\Security\Http\Event\InteractiveLoginEvent;

public function registerAction()
{
    // ...
    if ($this->get("request")->getMethod() == "POST")
    {
        // ... Do any password setting here etc

        $em->persist($user);
        $em->flush();

        // Here, "public" is the name of the firewall in your security.yml
        $token = new UsernamePasswordToken($user, $user->getPassword(), "public", $user->getRoles());

        // For older versions of Symfony, use security.context here
        $this->get("security.token_storage")->setToken($token);

        // Fire the login event
        // Logging the user in above the way we do it doesn't do this automatically
        $event = new InteractiveLoginEvent($request, $token);
        $this->get("event_dispatcher")->dispatch("security.interactive_login", $event);

        // maybe redirect out here
    }
}

当您将令牌设置到上下文中时,最后触发的事件不会自动完成,而通常在使用登录表单或类似表单时会自动完成。因此,将其包含在此处的原因。您可能需要根据您的使用案例调整使用的令牌类型 - 上面显示的是核心令牌,但如果需要,您可以使用其他令牌。UsernamePasswordToken

编辑:根据Franco在下面的评论,调整了上面的代码来解释“public”参数,并将用户的角色添加到令牌创建中。


答案 2

接受的版本不适用于 symfony 3.3。用户将在下一个请求中进行身份验证,而不是在当前请求中进行身份验证。原因是 ContextListener 检查上一个会话是否存在,如果不存在,它将清除安全 TokenStorage。解决这个问题的唯一方法(黑客如地狱)是通过手动初始化当前请求上的会话(和cookie)来伪造上一个会话的存在。

如果您找到更好的解决方案,请告诉我。

顺便说一句,我不确定这是否应该与公认的解决方案合并。

private function logUserIn(User $user)
{
    $token = new UsernamePasswordToken($user, null, "common", $user->getRoles());
    $request = $this->requestStack->getMasterRequest();

    if (!$request->hasPreviousSession()) {
        $request->setSession($this->session);
        $request->getSession()->start();
        $request->cookies->set($request->getSession()->getName(), $request->getSession()->getId());
    }

    $this->tokenStorage->setToken($token);
    $this->session->set('_security_common', serialize($token));

    $event = new InteractiveLoginEvent($this->requestStack->getMasterRequest(), $token);
    $this->eventDispatcher->dispatch("security.interactive_login", $event);
}

上面的代码假定您的防火墙名称(或共享上下文名称)为 。common


推荐