【问题标题】:Symfony2 links with CSRF tokenSymfony2 与 CSRF 令牌链接
【发布时间】:2012-08-11 22:13:50
【问题描述】:

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

现在你可以在 twig 中使用:

{{ 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.');
}

【问题讨论】:

    标签: php symfony


    【解决方案1】:

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

    【讨论】:

      【解决方案2】:

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

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

      比从树枝文件中您可以访问令牌为

      var token = '{{csrfToken}}'
      

      【讨论】:

      • 谢谢,我接受你的回答。但我的问题是如何生成链接而不传递额外的参数来查看?我已经为此创建了一个树枝扩展
      • 然后您可以制作一个表单,该表单在隐藏字段中包含令牌并在传递令牌时链接到您的 deleteAction,或者您可以发出 ajax 请求(如果您不希望用户被重定向) 其中包含令牌
      猜你喜欢
      • 2015-04-04
      • 1970-01-01
      • 2015-10-25
      • 2012-12-15
      • 2017-06-20
      • 1970-01-01
      • 2015-08-04
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多