Symfony2与CSRF令牌链接

时间:2012-08-15 15:28:39

标签: php symfony

我找不到有关如何使用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?'}) }}

我是控制器:

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

2 个答案:

答案 0 :(得分:3)

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

Twig模板:

<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
    }
}

答案 1 :(得分:2)

我有同样的问题。首先,我在Controller中生成令牌并传递给twig文件

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

 return array('csrfToken'=>$csrfToken);

而不是来自twig文件,你可以访问令牌

var token = '{{csrfToken}}'