使用嵌套的AJAX调用时获取TokenMismatchException。 Laravel 5.4

时间:2017-09-22 11:53:00

标签: php jquery ajax laravel-5 csrf

使用嵌套的AJAX调用时得到TokenMismatchException。第一个AJAX调用工作正常,但第二个调用总是出错而不是成功。

我想要做的是,当用户从导航栏中的按钮注册时,我希望他转到仪表板或/home - 这样可以正常工作。但是,当用户填写索引页面上的表格(买东西)时,我希望他:

检查他的输入是否有效,然后检查他是否已登录,如果没有,则弹出注册模式。在他注册后我希望他被重定向到结账页面。

然而,当用户填写购买表格并点击提交时,第一个ajax检查购买表格中的输入是否有效,如果是,则检查他是否登录,如果没有返回401错误。

401被第一个ajax拾取并将流程引导到401处理,其中注册模式弹出以进行注册,即第二个ajax弹出时。在他注册后,由于CSRF令牌不匹配,后端保持返回500。

首先,这是嵌套的ajax:

<script type="text/javascript">
    $(document).ready(function(){
        $('#topup-form').submit(function(e){
            e.preventDefault();
            var topup_info = $('form').serialize();

            //FIRST AJAX
            $.ajax({
                url: $('form').attr('action'),
                method: 'post',
                data: topup_info,
                type: 'json',


                //if success show success message for user
                success: function(result){
                    alert(result.responseJSON.code);
                    $('.alert.error').slideUp(200);
                    $('.alert.success').append("<p class='lead'>Thanks! To checkout we go!</p>").slideDown(200);
                },


                //for error check if it's 400 (validation) or 401(authentication)
                error: function(errorData){
                    // alert(errorData.responseJSON.code);
                    if(errorData.responseJSON.code === 400){
                        var error = errorData.responseJSON.message;
                        $('.alert.error').text('');
                        $('.alert.success').slideUp(200);


                        for (var i in error){
                            for (var j in error[i]) {
                                var message = error[i][j];
                                $('.alert.error').append("<p class='lead'>" + message + "<p>");
                            }
                        }

                        $('.alert.error').slideDown(00);
                    }//end error 400



                    //for authentication failure, show registeration modal
                    else if (errorData.responseJSON.code === 401) {

                        //change somethings in registeration modal
                        $('#myModalLabel').html('Please Login First');
                        $('#register').trigger('click');
                        document.getElementById('formRegister').action = "{{ route('user.regtopup') }}";


                        //when registeration form is submitted..
                        $('#formRegister').submit(function(e){
                            e.preventDefault();

                            //fire 2nd ajax
                            $.ajax({
                                url: $('#formRegister').attr('action'),
                                method: 'post',
                                data: $('form').serialize(),
                                type: 'json',

                                success: function(result){
                                    alert('success!!!');
                                },


                                //it keeps going to error! complaining about csrf token mismatch
                                error: function(result){
                                    console.log(JSON.stringify(result));
                                },
                            })//end of 2nd ajax
                        });//end of 2nd submit
                    }//end of 401
                }//end of error
            });//end of first ajax
        });//end of first submit

        $.ajaxSetup({
            headers: {
                'X-CSRF-Token': $('meta[name="csrf-token"]').attr('content')
            }
        })
    });
</script>

其次,这是检查输入有效性的控制器,并在未注册时返回401:

public function etiPost(Request $request) {
        $validator = [
        'topupAmount'=> 'required|integer|between:10,500',
        'phonenumber'=> 'required|regex:/^05[602][0-9]{7}$/',
        ];

        $inputs = $request->all();

        Log::info($inputs);

        $validator = Validator::make($inputs, $validator);

        if($validator->fails()){
            return Response::json([
                'error' => true,
                'message' => $validator->messages(),
                'code' => 400
            ], 400);
        }

        elseif (Auth::check()) {
             return view('pages.checkout', compact('inputs')); 
         }

         else {

              return Response::json([
                'error' => true,
                'message' => "Please login first",
                'code' => 401
            ], 401); 
        }   
    }

这是重载的register方法,在注册成功时返回JSON。这是500返回的地方!当我记录返回的JSON时,它会以正常的200响应出现,但是它会向第二个ajax发出“Whoops”500错误!用户已在数据库中成功注册,但此方法返回500,该调用由ajax调用的error部分捕获。

    /**
 * Handle a registration request for the application (overloaded).
 *
 * @param  \Illuminate\Http\Request  $request
 * @return \Illuminate\Http\Response
 */
public function register(Request $request)
{
    $validator = $this->validator($request->all());

    if ($validator->fails()) {
        $this->throwValidationException(
            $request, $validator
        );
    }

    $this->guard()->login($this->create($request->all()));

    // return response()->json();
    return response()->json(['msg' => 'Success! You have been registered!'], 200);
} 

为简洁起见,我不会包含表单,但请放心,我在HTML的head中添加了所有CSRF输入标记和元标记。

为避免这种情况,我应该采取哪些不同的做法?第一个ajax工作,但第二个不工作。

2 个答案:

答案 0 :(得分:0)

为每个ajax调用设置标头标记

headers: { 'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content') },

另请注意,您必须在模板中添加mete标记

您可以在模板中添加这样的元令牌

<meta name="csrf-token" content="{{ csrf_token() }}">

如果您仍想禁用csrf令牌,那么

从CSRF保护中排除URI

有时您可能希望从CSRF保护中排除一组URI。例如,如果您使用Stripe处理付款并使用其webhook系统,则需要从CSRF保护中排除Stripe webhook处理程序路由,因为Stripe将不知道要向您的路由发送什么CSRF令牌。

通常,您应将这些类型的路由放在RouteServiceProvider应用于routes / web.php文件中所有路由的Web中间件组之外。但是,您也可以通过将其URI添加到VerifyCsrfToken中间件的$ except属性来排除路由:

<?php

namespace App\Http\Middleware;

use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken as BaseVerifier;

class VerifyCsrfToken extends BaseVerifier
{
    /**
     * The URIs that should be excluded from CSRF verification.
     *
     * @var array
     */
    protected $except = [
        'stripe/*',
    ];
}

如果您提供'stripe/*',,则代替'/*',它将禁用所有的令牌 有关更多详细信息:

https://laravel.com/docs/5.5/csrf#csrf-x-csrf-token

答案 1 :(得分:0)

请在ajax调用之前按照jquery中的代码进行操作

 $.ajaxSetup({
    headers: {
        'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
    }
});

不建议在meta中添加CSRF令牌,因为所有页面都不包含提交值的表单,因此请使用此解决方案,以便您只能将CSRF用于js透视图。

谢谢