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
Symfony2 AJAX Login
提问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_handler
and failure_handler
under form_login
in my security.yml
file.
我有一个示例,我尝试使用 Symfony2 和 FOSUserBundle 创建 AJAX 登录。我设置我自己success_handler
和failure_handler
在form_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 onAuthenticationSuccess
and onAuthenticationFailure
overrides to only be executed for XmlHttpRequests (AJAX requests) and to simply hand the execution back to the original handler if not.
我希望我的onAuthenticationSuccess
和onAuthenticationFailure
覆盖仅针对 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 $http
does 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。看到这个问题