Symfony中的默认CSRF防护是基于表单的(如果您使用提供的表单构建器,则会自动发生)。但是,对于AJAX请求,手动将CSRF令牌附加到每个HTTP请求然后手动检查每个请求是很繁琐的。
一种好的方法是将令牌作为HTTP标头嵌入,如this question的注释中所建议的那样。然后可以将jQuery配置为在每个请求中包含此标头described here。
我的问题是如何在Symfony中最好地处理这个问题?我可以使用Twig在每个页面中包含CSRF令牌,但是如何检查传入的请求以确保每个请求在标题中包含有效的令牌?
我知道如何使用控制器内的$request->headers->get('X-CSRF-Token')
来访问HTTP标头,但这仍然意味着必须在每个控制器中单独执行此检查。我应该在下落中添加这张支票,以便尽早发现它?
答案 0 :(得分:0)
我不是100%肯定,但我会使用BaseFormType并扩展它。
您的基本表单类型将使用表单事件PRE_SET_DATA侦听器,并在请求中查找标题,然后将该标题填充到_token
字段中。
//FormType
class BaseFormType extends AbstractType
{
protected $request;
public function __construct(Request $request)
{
$this->request = $request;
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
$request = $this->request;
$builder->addEventListener(FormEvents::PRE_SET_DATA, function(FormEvent $event) use($request){
$token = $request->headers->get('X-CSRF-Token');
if($token){
$form = $event->getForm();
$form->get('_token')->setData($token);
}
}
}
}
然后所有的FormTypes都将扩展它:
//YourCustomFormType
class YourCustomFormType extends BaseFormType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
parent::buildForm();
$form->add('name');
}
}
然后你的控制器动作
//Controller
public function sayMyNameAction(Request $request)
{
$name = new Name();
$form = $this->createForm(new YourCustomFormType($request),$name);
if($request->isMethod('POST'))
{
$form->handleRequest($request);
if($form->isValid()){
return new JsonResponse(array('say':$name->getName()));
}
}
}
或类似的东西。