0

私の問題があります。いくつかの手順でフォームを送信する必要があります。アプリフォームログインフォームがあります

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

ログインフォームをajaxで送信してからアプリフォームを送信したいのですが、そうするとエラーが発生します

CSRF トークンが無効です。フォームの再送信をお試しください

Javascript コード:

$('#form_login').submit(function(){
            event.preventDefault();

            $.post('./login_check', {   _csrf_token : $('#login_csrf_token').val() ,
                                        _username : $('#login_username').val() ,
                                        _password : $('#login_password').val(),
                                        _remember_me : $('#login_remember_me').val() },
                    function (data) {

                        console.log(data);
                        /**
                         * If the login was ok we submit the app form
                         */
                        if(data.success){
                            $('#app_form').submit();
                        }
                        /**
                         * Else we sow to the user the error
                         */
                        else{
                            $('#error_login').html(data.message);
                        }
                    }
            );
        });

ログインフォームは成功を返しますが、アプリフォームを送信すると$('#app_form').submit(); エラーは次のページに表示されます。

前もって感謝します :)

4

3 に答える 3

3

私が選択した解決策は、各 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);
        }
    }
}

AJAX を使用する場合の失敗と成功の両方のログインで、応答に新しいトークンを追加します。

AJAX 呼び出しの Succes: function () で、javascript の病棟の後、フィールドを更新するだけです。

そんな感じ。

var route = $('#loginUrl').val();
var data= '_username=' +       $("#username").val()+"&_password="+$('#password').val()+"&_csrf_token="+$('#csrf_token').val();
$.ajax( {
    type : "POST",
    url : route,
    data: data,
    dataType: 'json',
    cache : false,
    success : function(response, value){
        //Response.success came from the JSON defined in the AjaxAuthenticationListener, he's not linked to the AJAX call
        if(response.success==true)
        {
        }

        $('#csrf_token').val(response.token); //Token has to be regenerate
        $(".tokenField").val(response.token);
    }
});

私はそれがより簡単な解決策だと思います。

于 2012-10-08T15:16:14.720 に答える
0

使用$('form').serialize():

$('#form_login').submit(function(){
    $.ajax({
        url:    $(this).attr('action'),
        method: $(this).attr('method'),
        data:   $(this).serialize(),
        success: function(response){
            alert(response);
        }
    });
});

コントローラーでは、次のように使用します。

  • return new Response('ok')良い反応と
  • return new Response('bad :(', 500)対応が悪いので
于 2014-05-21T21:06:13.143 に答える
0

CSRF トークンは、部分的に Cookie を使用します。1 つのページで 2 つの CSRF を生成し、フォームの 1 つを送信すると、Cookie が無効になります。

フレームワーク自体にいくつかの拡張機能がなければ、これを回避する方法は 1 つしかありません。

できることは、アプリ フォームを生成するコントローラーをセットアップすることです。

最初のページの読み込み時に、コントローラーはログイン フォームとアプリ フォームを読み込みます。AJAX 経由でログイン フォームを送信すると、アプリ フォームのコントローラーのみを要求します (ユーザーに新しい Cookie も提供します)。JavaScript を使用すると、新しいフォームから新しい csrf トークンを抽出し、それを元のアプリ フォームに挿入できます。次に、アプリ フォームを送信すると、新しい有効な csrf トークンが含まれている必要があります。

説明する:

アプリ フォームとログイン フォームを取得 -> AJAX 経由でログインを送信 -> バックグラウンドで AJAX 経由でアプリ フォームを取得 -> 新しいアプリ フォームの csrf トークンを盗み、最初のアプリ フォームに挿入 -> アプリ フォームを送信。

于 2012-07-11T16:09:21.013 に答える