php Symfony2 AJAX 登录

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/8607212/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-26 05:04:20  来源:igfitidea点击:

Symfony2 AJAX Login

phpsymfonyfosuserbundle

提问by leek

I have an example where I am trying to create an AJAX login using Symfony2 and FOSUserBundle. I am setting my own success_handlerand failure_handlerunder form_loginin my security.ymlfile.

我有一个示例,我尝试使用 Symfony2 和 FOSUserBundle 创建 AJAX 登录。我设置我自己success_handlerfailure_handlerform_login我的security.yml文件。

Here is the class:

这是课程:

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;
        }
    }
}

This works great for handling both successful and failed AJAX login attempts. However, when enabled - I am unable to login via the standard form POST method (non-AJAX). I receive the following error:

这对于处理成功和失败的 AJAX 登录尝试都非常有效。但是,启用后 - 我无法通过标准表单 POST 方法(非 AJAX)登录。我收到以下错误:

Catchable Fatal Error: Argument 1 passed to Symfony\Component\HttpKernel\Event\GetResponseEvent::setResponse() must be an instance of Symfony\Component\HttpFoundation\Response, null given

Catchable Fatal Error: Argument 1 passed to Symfony\Component\HttpKernel\Event\GetResponseEvent::setResponse() must be an instance of Symfony\Component\HttpFoundation\Response, null given

I'd like for my onAuthenticationSuccessand onAuthenticationFailureoverrides to only be executed for XmlHttpRequests (AJAX requests) and to simply hand the execution back to the original handler if not.

我希望我的onAuthenticationSuccessonAuthenticationFailure覆盖仅针对 XmlHttpRequests(AJAX 请求)执行,如果不是,则简单地将执行交还给原始处理程序。

Is there a way to do this?

有没有办法做到这一点?

TL;DR I want AJAX requested login attempts to return a JSON response for success and failure but I want it to not affect standard login via form POST.

TL;DR 我希望 AJAX 请求的登录尝试返回成功和失败的 JSON 响应,但我希望它不影响通过表单 POST 的标准登录。

回答by semateos

David's answeris good, but it's lacking a little detail for newbs - so this is to fill in the blanks.

大卫的回答很好,但是对于新手来说缺少一些细节 - 所以这是为了填补空白。

In addition to creating the AuthenticationHandler you'll need to set it up as a service using the service configuration in the bundle where you created the handler. The default bundle generation creates an xml file, but I prefer yml. Here's an example services.yml file:

除了创建 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' }

You'd need to modify the DependencyInjection bundle extension to use yml instead of xml like so:

您需要修改 DependencyInjection 包扩展以使用 yml 而不是 xml,如下所示:

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

$loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
$loader->load('services.yml');

Then in your app's security configuration you set up the references to the authentication_handler service you just defined:

然后在您的应用程序的安全配置中设置对您刚刚定义的 authentication_handler 服务的引用:

# 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

回答by David Morales

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);
        }
    }
}

回答by user2048716

If you want the FOS UserBundle form error support, you must use:

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

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

instead of:

代替:

$request->getSession()->setFlash('error', $exception->getMessage());

In the first answer.

在第一个答案中。

(of course remember about the header: use Symfony\Component\Security\Core\SecurityContext;)

(当然记住头文件:使用 Symfony\Component\Security\Core\SecurityContext;)

回答by stoefln

I handled this entirely with javascript:

我完全用javascript处理了这个:

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;
        });
    }

Here is the AjaxFormDialog class. Unfortunately I have not ported it to a jQuery plugin by now... https://gist.github.com/1601803

这是 AjaxFormDialog 类。不幸的是,我现在还没有将它移植到 jQuery 插件...... https://gist.github.com/1601803

回答by DaveT

You must return a Response object in both case (Ajax or not). Add an `else' and you're good to go.

在这两种情况下(Ajax 与否),您都必须返回一个 Response 对象。添加一个“else”,你就可以开始了。

The default implementation is:

默认实现是:

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

in AbstractAuthenticationListener::onSuccess

AbstractAuthenticationListener::onSuccess

回答by Divi

I made a little bundle for new users to provide an AJAX login form : https://github.com/Divi/AjaxLoginBundle

我为新用户制作了一个小包以提供 AJAX 登录表单:https: //github.com/Divi/AjaxLoginBundle

You just have to replace to form_loginauthentication by ajax_form_loginin the security.yml.

您只需要在security.yml中用ajax_form_login替换form_login身份验证。

Feel free to suggest new feature in the Github issue tracker !

随意在 Github 问题跟踪器中推荐新功能!

回答by Sehael

This may not be what the OP asked, but I came across this question, and thought others might have the same problem that I did.

这可能不是 OP 所问的,但我遇到了这个问题,并认为其他人可能和我有同样的问题。

For those who are implementing an AJAX login using the method that is described in the accepted answer and who are ALSO using AngularJS to perform the AJAX request, this won't work by default. Angular's $httpdoes not set the headers that Symfony is using when calling the $request->isXmlHttpRequest()method. In order to use this method, you need to set the appropriate header in the Angular request. This is what I did to get around the problem:

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

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

Before you use this method, be aware that this header does not work well with CORS. See this question

在使用此方法之前,请注意此标头不适用于 CORS。看到这个问题