我有一个示例,我尝试使用Symfony2和FOSUserBundle创建AJAX登录。我在success_handler
文件的failure_handler
下设置了自己的form_login
和security.yml
。
这是班级:
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 Fatal Error: Argument 1 passed to Symfony\Component\HttpKernel\Event\GetResponseEvent::setResponse() must be an instance of Symfony\Component\HttpFoundation\Response, null given
我希望我的onAuthenticationSuccess
和onAuthenticationFailure
覆盖仅针对XmlHttpRequests(AJAX请求)执行,如果没有,则只需将执行权交还给原始处理程序。
有办法做到这一点吗?
TL; DR我希望AJAX请求登录尝试返回成功和失败的JSON响应,但我希望它不会影响通过表单POST的标准登录。
答案 0 :(得分:50)
大卫的answer很好,但是缺少一些新手的细节 - 所以这就是填补空白。
除了创建AuthenticationHandler之外,您还需要使用创建处理程序的包中的服务配置将其设置为服务。默认的bundle生成创建了一个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');
然后在您的应用程序的安全配置中设置对刚刚定义的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
答案 1 :(得分:30)
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);
}
}
}
答案 2 :(得分:4)
如果您希望支持FOS UserBundle表单错误,则必须使用:
$request->getSession()->set(SecurityContext::AUTHENTICATION_ERROR, $exception);
而不是:
$request->getSession()->setFlash('error', $exception->getMessage());
在第一个答案中。
(当然记住标题:使用Symfony \ Component \ Security \ Core \ SecurityContext;)
答案 3 :(得分:3)
我用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;
});
}
这是AjaxFormDialog类。不幸的是,我现在还没有把它移植到jQuery插件...... https://gist.github.com/1601803
答案 4 :(得分:2)
您必须在两种情况下都返回Response对象(Ajax与否)。添加一个“其他”,你就可以了。
默认实现是:
$response = $this->httpUtils->createRedirectResponse($request, $this->determineTargetUrl($request));
AbstractAuthenticationListener::onSuccess
中的
答案 5 :(得分:1)
我为新用户提供了一个小包,以提供AJAX登录表单:https://github.com/Divi/AjaxLoginBundle
您只需要通过 security.yml 中的 ajax_form_login 替换为 form_login 身份验证。
随意在Github问题跟踪器中推荐新功能!
答案 6 :(得分:0)
这可能不是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 question