【问题标题】:Getting TokenMismatchException when using nested AJAX calls. Laravel 5.4使用嵌套 AJAX 调用时获取 TokenMismatchException。拉拉维尔 5.4
【发布时间】:2018-03-04 00:00:29
【问题描述】:

我在使用嵌套 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 的“哎呀”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 有效,但第二个无效。

【问题讨论】:

    标签: php jquery ajax laravel-5 csrf


    【解决方案1】:

    为每个 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

    【讨论】:

    • 谢谢,但我已经添加了 $.ajaxSetup,当我排除 URI 时收到此错误:{"readyState":4,"responseText":"{\"email\":[\"The电子邮件已被使用。\"],\"密码\":[\"密码确认不匹配。\"]}","re​​sponseJSON":{"email":["电子邮件已被使用。 "],"password":["密码确认不匹配。"]},"status":422,"statusText":"Unprocessable Entity"}
    • 现在这是验证错误。laravel 中的所有验证错误都会抛出 422 错误。如果有用,请将 answr 标记为已接受
    【解决方案2】:

    在调用ajax之前请关注jquery中的代码

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

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

    谢谢

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2019-03-14
      • 2018-11-13
      • 2018-10-14
      • 2017-10-06
      • 1970-01-01
      • 2017-10-02
      • 2018-06-08
      相关资源
      最近更新 更多