Php Symfony2 AJAX登录

Php Symfony2 AJAX登录,php,symfony,fosuserbundle,Php,Symfony,Fosuserbundle,我举了一个例子,试图使用Symfony2和FOSUserBundle创建AJAX登录。我正在我的security.yml文件中的form\u login下设置我自己的success\u handler和failure\u handler 下面是课堂: class AjaxAuthenticationListener implements AuthenticationSuccessHandlerInterface, AuthenticationFailureHandlerInterface {

我举了一个例子,试图使用Symfony2和FOSUserBundle创建AJAX登录。我正在我的
security.yml
文件中的
form\u login
下设置我自己的
success\u handler
failure\u handler

下面是课堂:

class AjaxAuthenticationListener implements AuthenticationSuccessHandlerInterface, AuthenticationFailureHandlerInterface
{  
    /**
     * This is called when an interactive authentication attempt succeeds. This
     * is called by authentication listeners inheriting from
     * AbstractAuthenticationListener.
     *
     * @see \Symfony\Component\Security\Http\Firewall\AbstractAuthenticationListener
     * @param Request        $request
     * @param TokenInterface $token
     * @return Response the response to return
     */
    public function onAuthenticationSuccess(Request $request, TokenInterface $token)
    {
        if ($request->isXmlHttpRequest()) {
            $result = array('success' => true);
            $response = new Response(json_encode($result));
            $response->headers->set('Content-Type', 'application/json');
            return $response;
        }
    }

    /**
     * This is called when an interactive authentication attempt fails. This is
     * called by authentication listeners inheriting from
     * AbstractAuthenticationListener.
     *
     * @param Request                 $request
     * @param AuthenticationException $exception    
     * @return Response the response to return
     */
    public function onAuthenticationFailure(Request $request, AuthenticationException $exception)
    {
        if ($request->isXmlHttpRequest()) {
            $result = array('success' => false, 'message' => $exception->getMessage());
            $response = new Response(json_encode($result));
            $response->headers->set('Content-Type', 'application/json');
            return $response;
        }
    }
}
这对于处理成功和失败的AJAX登录尝试非常有效。但是,如果启用,我将无法通过标准表单POST方法(非AJAX)登录。我收到以下错误:

Catchable致命错误:传递给Symfony\Component\HttpKernel\Event\GetResponseEvent::setResponse()的参数1必须是Symfony\Component\HttpFoundation\Response的实例,给定为空

我希望我的
onAuthenticationSuccess
onAuthenticationFailure
重写只对XmlHttpRequests(AJAX请求)执行,如果不执行,只需将执行交回原始处理程序即可

有办法做到这一点吗


TL;DR I希望AJAX请求的登录尝试返回JSON响应,无论成功还是失败,但我希望它不会影响通过form POST进行的标准登录。

在这两种情况下(AJAX与否),您都必须返回响应对象。加上一个'else',你就可以开始了

默认实现是:

$response = $this->httpUtils->createRedirectResponse($request, $this->determineTargetUrl($request));

AbstractAuthenticationListener::onSuccess

中,我完全用javascript处理了这个问题:

namespace YourVendor\UserBundle\Handler;

use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Bundle\FrameworkBundle\Routing\Router;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Security\Http\Authentication\AuthenticationSuccessHandlerInterface;
use Symfony\Component\Security\Http\Authentication\AuthenticationFailureHandlerInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationException;

class AuthenticationHandler
implements AuthenticationSuccessHandlerInterface,
           AuthenticationFailureHandlerInterface
{
    private $router;

    public function __construct(Router $router)
    {
        $this->router = $router;
    }

    public function onAuthenticationSuccess(Request $request, TokenInterface $token)
    {
        if ($request->isXmlHttpRequest()) {
            // Handle XHR here
        } else {
            // If the user tried to access a protected resource and was forces to login
            // redirect him back to that resource
            if ($targetPath = $request->getSession()->get('_security.target_path')) {
                $url = $targetPath;
            } else {
                // Otherwise, redirect him to wherever you want
                $url = $this->router->generate('user_view', array(
                    'nickname' => $token->getUser()->getNickname()
                ));
            }

            return new RedirectResponse($url);
        }
    }

    public function onAuthenticationFailure(Request $request, AuthenticationException $exception)
    {
        if ($request->isXmlHttpRequest()) {
            // Handle XHR here
        } else {
            // Create a flash message with the authentication error message
            $request->getSession()->setFlash('error', $exception->getMessage());
            $url = $this->router->generate('user_login');

            return new RedirectResponse($url);
        }
    }
}
if($('a.login').length > 0) { // if login button shows up (only if logged out)
        var formDialog = new MyAppLib.AjaxFormDialog({ // create a new ajax dialog, which loads the loginpage
            title: 'Login',
            url: $('a.login').attr('href'),
            formId: '#login-form',
            successCallback: function(nullvalue, dialog) { // when the ajax request is finished, look for a login error. if no error shows up -> reload the current page
                if(dialog.find('.error').length == 0) {
                    $('.ui-dialog-content').slideUp();
                    window.location.reload();
                }
            }
        });

        $('a.login').click(function(){
            formDialog.show();
            return false;
        });
    }
这是AjaxFormDialog类。不幸的是,我现在还没有将它移植到jQuery插件中

David's不错,但对新手来说,它缺少一点细节——所以这是为了填补空白

除了创建AuthenticationHandler之外,还需要使用创建处理程序的包中的服务配置将其设置为服务。默认的包生成会创建一个xml文件,但我更喜欢yml。下面是一个示例services.yml文件:

#src/Vendor/BundleName/Resources/config/services.yml

parameters:
    vendor_security.authentication_handler: Vendor\BundleName\Handler\AuthenticationHandler

services:
    authentication_handler:
        class:  %vendor_security.authentication_handler%
        arguments:  [@router]
        tags:
            - { name: 'monolog.logger', channel: 'security' }
您需要修改DependencyInjection捆绑包扩展以使用yml而不是xml,如下所示:

#src/Vendor/BundleName/DependencyInjection/BundleExtension.php

$loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
$loader->load('services.yml');
然后在应用程序的安全配置中,设置对刚才定义的身份验证处理程序服务的引用:

# app/config/security.yml

security:
    firewalls:
        secured_area:
            pattern:    ^/
            anonymous: ~
            form_login:
                login_path:  /login
                check_path:  /login_check
                success_handler: authentication_handler
                failure_handler: authentication_handler

如果需要FOS UserBundle表单错误支持,必须使用:

$request->getSession()->set(SecurityContext::AUTHENTICATION_ERROR, $exception);
而不是:

$request->getSession()->setFlash('error', $exception->getMessage());
在第一个答案中


(当然记得标题:使用Symfony\Component\Security\Core\SecurityContext;)

我为新用户制作了一个小捆绑包,以提供AJAX登录表单:

您只需在security.yml中通过ajax\u form\u login替换为form\u login身份验证即可


请随时在Github问题跟踪程序中推荐新功能

这可能不是OP所问的问题,但我遇到了这个问题,并认为其他人可能会遇到与我相同的问题

对于那些使用公认答案中描述的方法实现AJAX登录的人,以及使用AngularJS执行AJAX请求的人,默认情况下,这将不起作用。Angular的
$http
没有设置Symfony在调用
$request->isXmlHttpRequest()
方法时使用的头。为了使用此方法,您需要在Angular请求中设置适当的标头。这就是我为解决这个问题所做的:

$http({
    method  : 'POST',
    url     : {{ path('login_check') }},
    data    : data,
    headers: {'X-Requested-With': 'XMLHttpRequest'}
})

在使用此方法之前,请注意此标头不能很好地用于CORS。请参见

,问题是我无法访问
$this->httpUtils
的实例。另外,我只想将执行传递回
AbstractAuthenticationListener
-我不想将代码从中复制并粘贴到我的处理程序中。我希望您能找到一个好方法来执行,因为我和您一样被困在这一点上;-)我也有同样的问题。我在这里发布了一个问题:但目前没有答案:(这里有一篇很好的文章解释了如何做到这一点:这段代码由@elnur回答了我提出的一个类似问题。这是一个很好的回答-很抱歉没有看到您的类似问题。但是,我不会将其标记为已接受,因为我只想在当前请求是XMLHttpRequest时修改响应——否则,我会修改它。)想让Symfony假装我从未被拦截过。这是最简单的部分。你只需要返回一个包含你喜欢的内容的json响应,然后通过jquery阅读它。我认为最困难的是(遗憾的是没有记录)部分是非ajax登录处理程序。这是最好的答案,使用它我基本上可以完成我需要的-所以谢谢!注意,这个示例缺少以下内容,但我还没有编辑权限:-(使用Symfony\Component\Security\Core\Exception\AuthenticationException;services.yml配置中有一个输入错误。逗号(,)在频道之前需要。-{name:'monolog.logger',频道:'security'}谢谢这确实有助于填补Davids答案中的空白!好主意,即使它不干净(接受的答案是处理此问题的最佳方式),也会有帮助。