Php Symfony 2 Ajax提交,令牌CSRF无效
这是我的问题,我需要分几个步骤提交表格。我有一个应用程序表单和一个登录表单Php Symfony 2 Ajax提交,令牌CSRF无效,php,jquery,symfony,symfony-forms,Php,Jquery,Symfony,Symfony Forms,这是我的问题,我需要分几个步骤提交表格。我有一个应用程序表单和一个登录表单 <form id="app_form" action="{{ path('app_create') }}" method="post" {{ form_enctype(formApp) }}> <div class="row-fluid"> <div class="span2">{{ form_label(formApp.name, 'Name
<form id="app_form" action="{{ path('app_create') }}" method="post" {{ form_enctype(formApp) }}>
<div class="row-fluid">
<div class="span2">{{ form_label(formApp.name, 'Name'|trans) }}</div>
<div class="span4">{{ form_widget(formApp.name, { required : true }) }}</div>
<div class="span2" id="error_app_name">{{ form_errors(formApp.name) }}</div>
</div>
<div class="row-fluid">
<div class="span2">{{ form_label(formApp.description, 'Description'|trans) }}</div>
<div class="span4">{{ form_widget(formApp.description, { required : true }) }}</div>
<div class="span2">{{ form_errors(formApp.description) }}</div>
</div>
<div class="row-fluid">
<div class="span2">{{ form_label(formApp.iosUrl, 'iOS'|trans) }}</div>
<div class="span4">{{ form_widget(formApp.iosUrl) }}</div>
<div class="span2">{{ form_errors(formApp.iosUrl) }}</div>
</div>
<div class="row-fluid">
<div class="span2">{{ form_label(formApp.androidBundle, 'Android Bundle'|trans) }}</div>
<div class="span4">{{ form_widget(formApp.androidBundle) }}</div>
<div class="span2">{{ form_errors(formApp.androidBundle) }}</div>
</div>
<div class="row-fluid">
<div class="span2">{{ form_label(formApp.wpUrl, 'Windows Phone'|trans) }}</div>
<div class="span4">{{ form_widget(formApp.wpUrl) }}</div>
<div class="span2">{{ form_errors(formApp.wpUrl) }}</div>
</div>
<div class="row-fluid">
<div class="span2">{{ form_label(formApp.bbUrl, 'Blackberry'|trans) }}</div>
<div class="span4">{{ form_widget(formApp.bbUrl) }}</div>
<div class="span2">{{ form_errors(formApp.bbUrl) }}</div>
</div>
<div class="row-fluid">
<div class="span2">{{ form_label(formApp.fallbackUrl, 'Fallback Url'|trans) }}</div>
<div class="span4">{{ form_widget(formApp.fallbackUrl) }}</div>
<div class="span2">{{ form_errors(formApp.fallbackUrl) }}</div>
</div>
{{ form_rest(formApp) }}
<button type="submit" class="btn">{{ 'Next step'|trans }}</button>
</form>
<form id="form_login">
<input type="hidden" id="login_csrf_token" name="_csrf_token" value="{{ csrf_token }}" />
<label for="login_username">{{ 'security.login.username'|trans({}, 'FOSUserBundle') }}</label>
<input type="text" id="login_username" name="_username" value="{{ last_username }}" required="true" />
<label for="login_password">{{ 'security.login.password'|trans({}, 'FOSUserBundle') }}</label>
<input type="password" id="login_password" name="_password" required="true" />
<input type="checkbox" id="login_remember_me" name="_remember_me" value="on" />
<label for="login_remember_me">{{ 'security.login.remember_me'|trans({}, 'FOSUserBundle') }}</label>
<button type="submit" class="btn">{{ 'Login'|trans }}</button>
<button class="btn" id="register_show" type="button">{{ 'Want to register ?'|trans }}</button>
<div id="error_login"></div>
</form>
登录表单返回成功,但当我提交应用程序表单时,$(“#应用程序表单”).submit()错误将出现在下一页中
提前感谢:)CSRF令牌部分使用cookies。当您在一个页面上生成两个CSRFs并提交其中一个表单时,您将使cookie无效 如果没有对框架本身进行一些扩展,我只能看到解决这个问题的一种方法——这是相当迂回的: 你可以做的是设置一个控制器来生成你的应用程序表单 在初始页面加载时,控制器将加载登录表单和应用表单。在通过AJAX提交登录表单时,您还将只请求应用表单的控制器(这也将给用户一个新的cookie)。使用javascript,您可以从新表单中提取新的csrf令牌,并将其注入原始应用表单。然后,当您提交应用程序表单时,它应该有一个新的、有效的csrf令牌 举例说明:
获取应用程序表单和登录表单->通过AJAX提交登录->在后台通过AJAX获取应用程序表单->窃取新应用程序表单的csrf令牌并将其注入第一个应用程序表单->提交应用程序表单。csrf令牌部分使用cookie。当您在一个页面上生成两个CSRFs并提交其中一个表单时,您将使cookie无效 如果没有对框架本身进行一些扩展,我只能看到解决这个问题的一种方法——这是相当迂回的: 你可以做的是设置一个控制器来生成你的应用程序表单 在初始页面加载时,控制器将加载登录表单和应用表单。在通过AJAX提交登录表单时,您还将只请求应用表单的控制器(这也将给用户一个新的cookie)。使用javascript,您可以从新表单中提取新的csrf令牌,并将其注入原始应用表单。然后,当您提交应用程序表单时,它应该有一个新的、有效的csrf令牌 举例说明:
获取应用程序表单和登录表单->通过AJAX提交登录->在后台通过AJAX获取应用程序表单->窃取新应用程序表单的csrf令牌并将其注入第一个应用程序表单->提交应用程序表单。我选择的解决方案是在每个LoginAjax操作中重新生成令牌,并在响应中发送它(以JSON格式)。然后我只需要更新所有具有令牌字段的表单(可以通过为每个令牌设置特定类来使用) 实现AuthenticationSuccessHandler的第一步 定义为
acme.security.ajax_handler:
class: Acme\UserBundle\Listener\AjaxAuthenticationListener
arguments: ["@form.csrf_provider", "@router"]
AjaxAuthenticationListener类
<?php
namespace Travelyo\UserBundle\Listener;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Security\Http\Authentication\AuthenticationFailureHandlerInterface;
use Symfony\Component\Security\Http\Authentication\AuthenticationSuccessHandlerInterface;
/**
* Refere to http://stackoverflow.com/questions/8607212/symfony2-ajax-login
* @author yoni
*
*/
class AjaxAuthenticationListener implements AuthenticationSuccessHandlerInterface, AuthenticationFailureHandlerInterface {
protected $csrf_provider;
protected $router;
/**
* In case we have Failure I need to provide a new csrf token
* @param unknown_type $csrf_provider
* @author Yoni Alhadeff
*/
public function __construct($csrf_provider, $router)
{
$this->csrf_provider = $csrf_provider;
$this->router = $router;
}
/**
* This is called when an interactive authentication attempt succeeds. This
* is called by authentication listeners inheriting from
* AbstractAuthenticationListener.
*
* @see SymfonyComponentSecurityHttpFirewallAbstractAuthenticationListener
* @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, 'token'=>$this->csrf_provider->generateCsrfToken('unknown'));
$response = new Response(json_encode($result));
$response->headers->set('Content-Type', 'application/json');
return $response;
} 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.account.target_path'))
{
$url = $targetPath;
} else
{
// Otherwise, redirect him to wherever you want
$url = $this->router->generate('homepage_route_name', array());
}
return new RedirectResponse($url);
}
}
/**
* 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, 'error'=>$exception->getMessage(),'token'=>$this->csrf_provider->generateCsrfToken('authenticate'));
$response = new Response(json_encode($result));
$response->headers->set('Content-Type', 'application/json');
return $response;
} else
{
// Create a flash message with the authentication error message
$request->getSession()->setFlash('error', $exception->getMessage());
$url = $this->router->generate('fos_frontend_security_login');
return new RedirectResponse($url);
}
}
}
我认为这是一个更简单的解决方案。我选择的解决方案是在每个LoginAjax操作中重新生成令牌,并在响应中发送(以JSON格式)。然后我只需更新所有具有令牌字段的表单(可以通过为每个令牌设置特定类来使用) 实现AuthenticationSuccessHandler的第一步 定义为
acme.security.ajax_handler:
class: Acme\UserBundle\Listener\AjaxAuthenticationListener
arguments: ["@form.csrf_provider", "@router"]
AjaxAuthenticationListener类
<?php
namespace Travelyo\UserBundle\Listener;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Security\Http\Authentication\AuthenticationFailureHandlerInterface;
use Symfony\Component\Security\Http\Authentication\AuthenticationSuccessHandlerInterface;
/**
* Refere to http://stackoverflow.com/questions/8607212/symfony2-ajax-login
* @author yoni
*
*/
class AjaxAuthenticationListener implements AuthenticationSuccessHandlerInterface, AuthenticationFailureHandlerInterface {
protected $csrf_provider;
protected $router;
/**
* In case we have Failure I need to provide a new csrf token
* @param unknown_type $csrf_provider
* @author Yoni Alhadeff
*/
public function __construct($csrf_provider, $router)
{
$this->csrf_provider = $csrf_provider;
$this->router = $router;
}
/**
* This is called when an interactive authentication attempt succeeds. This
* is called by authentication listeners inheriting from
* AbstractAuthenticationListener.
*
* @see SymfonyComponentSecurityHttpFirewallAbstractAuthenticationListener
* @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, 'token'=>$this->csrf_provider->generateCsrfToken('unknown'));
$response = new Response(json_encode($result));
$response->headers->set('Content-Type', 'application/json');
return $response;
} 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.account.target_path'))
{
$url = $targetPath;
} else
{
// Otherwise, redirect him to wherever you want
$url = $this->router->generate('homepage_route_name', array());
}
return new RedirectResponse($url);
}
}
/**
* 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, 'error'=>$exception->getMessage(),'token'=>$this->csrf_provider->generateCsrfToken('authenticate'));
$response = new Response(json_encode($result));
$response->headers->set('Content-Type', 'application/json');
return $response;
} else
{
// Create a flash message with the authentication error message
$request->getSession()->setFlash('error', $exception->getMessage());
$url = $this->router->generate('fos_frontend_security_login');
return new RedirectResponse($url);
}
}
}
我认为这是更简单的解决方案。使用$('form')。serialize()
:
在控制器使用中,请执行以下操作:
以获得良好的响应和返回新的响应('ok')
用于错误响应返回新响应('bad:(',500)
$('form')。序列化()
:
在控制器使用中,请执行以下操作:
以获得良好的响应和返回新的响应('ok')
用于错误响应返回新响应('bad:(',500)
$('#form_login').submit(function(){
$.ajax({
url: $(this).attr('action'),
method: $(this).attr('method'),
data: $(this).serialize(),
success: function(response){
alert(response);
}
});
});