Symfony2与CSRF令牌链接


Symfony2 links with CSRF token

我找不到有关如何使用CSRF令牌生成链接的文档,就像在Symfony 1.4中一样:

link_to(__('Delete'), url_for('ntw-delete', $network), array('confirm' => 'Are you sure?', 'method' => 'delete'))

更新:我为此创建了一个树枝扩展。也许它会对某人有所帮助

src/UmbrellaWeb/Bundle/ExtraTwigBundle/Twig/LinkExtension.php

<?php
namespace UmbrellaWeb'Bundle'ExtraTwigBundle'Twig;
use Twig_Extension;
use Twig_Function_Method;
use Twig_Environment;
use Symfony'Component'Form'Extension'Csrf'CsrfProvider'CsrfProviderInterface;
class LinkExtension extends Twig_Extension
{
    protected $csrfProvider;
    public function __construct(CsrfProviderInterface $csrfProvider)
    {
        $this->csrfProvider = $csrfProvider;
    }
    public function getFunctions()
    {
        return array(
            'link_to' => new Twig_Function_Method($this, 'linkToFunction', array(
                'is_safe' => array('html')
            ))
        );
    }
    /**
     * Build a link with anchor
     * 
     * @param string $path
     * @param string $title
     * @param array $options Available options: 
     *  string 'confirm' - Text for the popup
     *  string 'method' - HTTP Method: post, delete, put
     *  string 'csrfIntention' - CSRF intention. If empty then no CSRF. Not used for GET requests
     *  string 'csrfField' - CSRF field name. _token by default
     *  bool 'escape' - escape title, TRUE by default
     */
    public function linkToFunction($path,$title,array $options = array())
    {
        $default = array(
            'csrf_intention' => '',
            'csrf_field' => '_token',
            'escape' => TRUE
        );
        $options = array_merge($default,$options);
        $ecape = $options['escape'];
        unset($options['escape']);
        $return = '<a href="%s"%s>%s</a>';
        $return = sprintf($return,
            htmlspecialchars($path),
            $this->_tagOptions($this->_options2javascript($options)),
            ($ecape)?htmlspecialchars($title):$title
        );
        return $return;
    }
    function _options2javascript($options)
    {
        // confirm
        $confirm = isset($options['confirm']) ? $options['confirm'] : '';
        unset($options['confirm']);
        // method
        $method = isset($options['method']) ? $options['method'] : false;
        unset($options['method']);
        // CSRF Intention
        $csrfIntention = isset($options['csrf_intention']) ? $options['csrf_intention'] : false;
        unset($options['csrf_intention']);
        // CSRF field name
        $csrfField = isset($options['csrf_field']) ? $options['csrf_field'] : false;
        unset($options['csrf_field']);
        $onclick = isset($options['onclick']) ? $options['onclick'] : '';
        if ($confirm && $method)
        {
            $options['onclick'] = $onclick . 'if (' . $this->_confirmJsFunction($confirm) . ') { ' . $this->_methodJsFunction($method,$csrfIntention,$csrfField) . ' };return false;';
        } else 
            if ($confirm)
            {
                if ($onclick)
                {
                    $options['onclick'] = 'if (' . $this->_confirmJsFunction($confirm) . ') { return ' . $onclick . '} else return false;';
                } else
                {
                    $options['onclick'] = 'return ' . $this->_confirmJsFunction($confirm) . ';';
                }
            } else 
                if ($method)
                {
                    $options['onclick'] = $onclick . $this->_methodJsFunction($method,$csrfIntention,$csrfField) . 'return false;';
                }
        return $options;
    }
    function _confirmJsFunction($confirm)
    {
      return "confirm('".$this->_escapeJs($confirm)."')";
    }
    /**
     * Escape carrier returns and single and double quotes for Javascript segments.
     */
    function _escapeJs($javascript = '')
    {
        $javascript = preg_replace('/'r'n|'n|'r/', "''n", $javascript);
        $javascript = preg_replace('/(["''])/', ''''''1', $javascript);
        return $javascript;
    }
    function _methodJsFunction($method,$csrfIntention,$csrfField)
    {
        $function = "var f = document.createElement('form'); f.style.display = 'none'; this.parentNode.appendChild(f); f.method = 'post'; f.action = this.href;";
        //put, delete HTTP methods
        if ('post' != strtolower($method))
        {
            $function .= "var m = document.createElement('input'); m.setAttribute('type', 'hidden'); ";
            $function .= sprintf("m.setAttribute('name', '_method'); m.setAttribute('value', '%s'); f.appendChild(m);", strtolower($method));
        }
        // CSRF protection
        if ($csrfIntention)
        {
            /**
             * @todo isCsrfEnabled() - check a global config
             */
            if (TRUE)
            {
                $function .= "var m = document.createElement('input'); m.setAttribute('type', 'hidden'); ";
                $function .= sprintf("m.setAttribute('name', '%s'); m.setAttribute('value', '%s'); f.appendChild(m);", $csrfField, $this->csrfProvider->generateCsrfToken($csrfIntention));
            }
        }
        $function .= "f.submit();";
        return $function;
    }
    function _tagOptions(array $options = array())
    {
        $html = '';
        foreach ($options as $key => $value)
        {
            $html .= ' ' . $key . '="' . htmlspecialchars($value) . '"';
        }
        return $html;
    }
    public function getName()
    {
        return 'umbrellaweb_link';
    }
}

services.xml

<container xmlns="http://symfony.com/schema/dic/services"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
    <services>
        <service id="umbrellaweb.twig.link_extension" class="UmbrellaWeb'Bundle'ExtraTwigBundle'Twig'LinkExtension">
            <tag name="twig.extension" />
            <argument type="service" id="form.csrf_provider" />
        </service>
    </services>
</container>

现在在树枝中您可以使用:

{{ link_to(path('jk_aa_admin_delete',{'id' : admin.id}),'<img src="del_icon.png"/>',
{'escape':false,'method':'delete','csrf_intention':'delete-admin',
'confirm':'Are you sure?'}) }}

I 控制器:

//check CSRF token
if (FALSE === $this->get('form.csrf_provider')->isCsrfTokenValid('delete-admin', $request->get('_token')))
{
throw new AccessDeniedHttpException('Invalid CSRF token.');
}
来自

Symfony 4的解决方案,来自这里的文档 http://symfony.com/doc/current/security/csrf.html:

树枝模板:

<a href="{{ path("remove", { "id" : 1, "csrf_token" : csrf_token('remove') }) }}">
{{ "remove" | trans }}
</a>

控制器:

/**
 * @Route("/remove/{id}", name="dashboard_alert_remove")
 *
 * @ParamConverter("alert", class="App:Alert")
 *
 * @param Alert $alert
 */
public function doRemoveAction(Alert $alert, Request $request)
{
    $submittedToken = $request->get('csrf_token');
    if ($this->isCsrfTokenValid('remove', $submittedToken))
    {
        // Do the deletion stuff
    }
}

我遇到了同样的问题。 首先,我在控制器中生成令牌并传递给树枝文件

 $intentions = 'unknown';  
 $csrfToken = $this->container->get('form.csrf_provider')->generateCsrfToken($intentions);
 return array('csrfToken'=>$csrfToken);

比从 twig 文件访问令牌作为

var token = '{{csrfToken}}'