Обработка истекшего токена в Laravel


14 ов принято

работа для него, заключается в том, чтобы фактически получить новый токен в определенное время, иначе вы побеждаете цель токена csrf:

<html>
    <head>
        <meta name="csrf_token" content="{{ csrf_token() }}">
    </head>
    <body>
        <script type="text/javascript">
            var csrfToken = $('[name="csrf_token"]').attr('content');

            setInterval(refreshToken, 3600000); // 1 hour 

            function refreshToken(){
                $.get('refresh-csrf').done(function(data){
                    csrfToken = data; // the new token
                });
            }

            setInterval(refreshToken, 3600000); // 1 hour 

        </script>
    </body>
</html>

В маршрутах laravel

Route::get('refresh-csrf', function(){
    return csrf_token();
});

Я прошу прощения в случае каких-либо синтаксических ошибок, не использовал jquery в течение длительного времени, но я думаю, вы поняли идею


15

Я объединяю 2 вещи для этого случая:

1. Увеличьте сеанс // В config / session.php замените это: 'lifetime' => 120 // с: 'lifetime' => 360

//In app/Exceptions/Handler.php replace this:

public function render($request, Exception $e)
{
    if ($e instanceof ModelNotFoundException) {
        $e = new NotFoundHttpException($e->getMessage(), $e);
    }

    return parent::render($request, $e);
}

//with:

public function render($request, Exception $e)
{
    if ($e instanceof ModelNotFoundException) {
        $e = new NotFoundHttpException($e->getMessage(), $e);
    }

    if ($e instanceof IlluminateSessionTokenMismatchException) {            
        return redirect('/')->withErrors(['token_error' => 'Sorry, your session seems to have expired. Please try again.']);
    }

    return parent::render($request, $e);
}

Время жизни по умолчанию Laravel 5 равно 120 (минутам), вы можете изменить его на любое значение, например, 360 (6 часов)

2. Поймать исключение и отобразить сообщение об ошибке

@if ($errors->has('token_error'))
    {{ $errors->first('token_error') }}
@endif

Поэтому в основном вы перенаправляете пользователя на корень «/» (вы можете изменить его на любой путь) с сообщением об ошибке, и на этой странице вы должны сделать это, чтобы отобразить сообщение об ошибке:

web.php

10

Я думаю, что ответ от @UX Labs вводит в заблуждение. И тогда комментарий от @jfadich кажется совершенно неправильным.

Для Laravel 5.4 в мае 2017 года я решил проблему следующим образом:

Вот ответ, который работает

В Route::post('keep-token-alive', function() { return 'Token must have been valid, and the session expiration has been extended.'; //https://stackoverflow.com/q/31449434/470749 }); :

$(document).ready(function () {

    setInterval(keepTokenAlive, 1000 * 60 * 15); // every 15 mins

    function keepTokenAlive() {
        $.ajax({
            url: '/keep-token-alive', //https://stackoverflow.com/q/31449434/470749
            type: 'post',
            headers: {
                'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
            }
        }).then(function (result) {
            console.log(new Date() + ' ' + result + ' ' + $('meta[name="csrf-token"]').attr('content'));
        });
    }

});

В javascript, на ваш взгляд:

'keep-token-alive'

Обратите внимание, что вы не должны перечислять VerifyCsrfToken.phpисключения session.php. Как @ ITDesigns.eu подразумеваемого в комментариях, это очень важно для этого маршрута , чтобы убедиться , что есть действительный маркер в настоящее время , и что он просто должно быть его действие продлено.

Почему этот подход решает мою проблему

Мой сайт Laravel позволяет пользователям смотреть видео (час), и он использует ajax для публикации своего прогресса каждую минуту.

Но многие пользователи загружают страницу, а затем не запускают видео до много часов спустя.

Я не знаю, почему они оставляют свою вкладку браузера открытой до того, как смотреть, но они это делают.

И тогда я получу тонны исключений TokenMismatch в своих журналах (и пропустил бы данные о их прогрессе).

В 'lifetime', я изменился web.phpс 120 до 360 минут, но этого все еще было недостаточно. И я не хотел делать это дольше 6 часов. Поэтому мне нужно было включить эту страницу, чтобы часто расширять сеанс с помощью ajax.

Как вы можете проверить это и понять, как работают токены:

В Route::post('refresh-csrf', function() {//Note: as I mentioned in my answer, I think this approach from @UX Labs does not make sense, but I first wanted to design a test view that used buttons to ping different URLs to understand how tokens work. The "return csrf_token();" does not even seem to get used. return csrf_token(); }); Route::post('test-csrf', function() { return 'Token must have been valid.'; }); :

<button id="tryPost">Try posting to db</button>
<button id="getNewToken">Get new token</button>

(function () {
    var $ = require("jquery");

    $(document).ready(function () {
        $('body').prepend('<div>' + new Date() + ' Current token is: ' + $('meta[name="csrf-token"]').attr('content') + '</div>');
        $('#getNewToken').click(function () {
            $.ajax({
                url: '/refresh-csrf',
                type: 'post',
                headers: {
                    'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
                }
            }).then(function (d) {
                $('meta[name="csrf-token"]').attr('content', d);
                $('body').prepend('<div>' + new Date() + ' Refreshed token is: ' + $('meta[name="csrf-token"]').attr('content') + '</div>');
            });
        });
        $('#tryPost').click(function () {
            $.ajax({
                url: '/test-csrf',
                type: 'post',
                headers: {
                    'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
                }
            }).then(function (d) {
                $('body').prepend('<div>' + new Date() + ' Result of test: ' + d + '</div>');
            });
        });


    });
})();

В javascript, на ваш взгляд:

session.php

В 'lifetime', временно меняйте lifetimeчто-то очень короткое для целей тестирования.

Затем поиграйте.

Вот как я узнал, как работает токен Laravel и как нам просто нужно успешно отправлять POST на защищенный CSRF маршрут, чтобы токен оставался действительным.


1

Увеличьте lifetimeсвои сеансы. Вы можете сделать это, отредактировав config/session.phpфайл в вашей конфигурации laravel.

/*
|--------------------------------------------------------------------------
| Session Lifetime
|--------------------------------------------------------------------------
|
| Here you may specify the number of minutes that you wish the session
| to be allowed to remain idle before it expires. If you want them
| to immediately expire on the browser closing, set that option.
|
*/

'lifetime' => 120,

0

Я знаю, что это не лучшее решение, но одно из самых простых. Просто отключите защиту CSRF для этой страницы, где пользователь проводит много времени. Например, на моем сайте они могут писать статью часами на одной странице. И это очень неприятно, если вы не можете сохранить статью из-за защиты CSRF.

PHP, Laravel, Laravel-5,

php,laravel,laravel-5,

24

Ответов: 7


14 ов принято

работа для него, заключается в том, чтобы фактически получить новый токен в определенное время, иначе вы побеждаете цель токена csrf:

<html>
    <head>
        <meta name="csrf_token" content="{{ csrf_token() }}">
    </head>
    <body>
        <script type="text/javascript">
            var csrfToken = $('[name="csrf_token"]').attr('content');

            setInterval(refreshToken, 3600000); // 1 hour 

            function refreshToken(){
                $.get('refresh-csrf').done(function(data){
                    csrfToken = data; // the new token
                });
            }

            setInterval(refreshToken, 3600000); // 1 hour 

        </script>
    </body>
</html>

В маршрутах laravel

Route::get('refresh-csrf', function(){
    return csrf_token();
});

Я прошу прощения в случае каких-либо синтаксических ошибок, не использовал jquery в течение длительного времени, но я думаю, вы поняли идею


15

Я объединяю 2 вещи для этого случая:

1. Увеличьте сеанс // В config / session.php замените это: 'lifetime' => 120 // с: 'lifetime' => 360

//In app/Exceptions/Handler.php replace this:

public function render($request, Exception $e)
{
    if ($e instanceof ModelNotFoundException) {
        $e = new NotFoundHttpException($e->getMessage(), $e);
    }

    return parent::render($request, $e);
}

//with:

public function render($request, Exception $e)
{
    if ($e instanceof ModelNotFoundException) {
        $e = new NotFoundHttpException($e->getMessage(), $e);
    }

    if ($e instanceof IlluminateSessionTokenMismatchException) {            
        return redirect('/')->withErrors(['token_error' => 'Sorry, your session seems to have expired. Please try again.']);
    }

    return parent::render($request, $e);
}

Время жизни по умолчанию Laravel 5 равно 120 (минутам), вы можете изменить его на любое значение, например, 360 (6 часов)

2. Поймать исключение и отобразить сообщение об ошибке

@if ($errors->has('token_error'))
    {{ $errors->first('token_error') }}
@endif

Поэтому в основном вы перенаправляете пользователя на корень «/» (вы можете изменить его на любой путь) с сообщением об ошибке, и на этой странице вы должны сделать это, чтобы отобразить сообщение об ошибке:

web.php

10

Я думаю, что ответ от @UX Labs вводит в заблуждение. И тогда комментарий от @jfadich кажется совершенно неправильным.

Для Laravel 5.4 в мае 2017 года я решил проблему следующим образом:

Вот ответ, который работает

В Route::post('keep-token-alive', function() { return 'Token must have been valid, and the session expiration has been extended.'; //http://moredez.ru/q//q/31449434/470749 }); :

$(document).ready(function () {

    setInterval(keepTokenAlive, 1000 * 60 * 15); // every 15 mins

    function keepTokenAlive() {
        $.ajax({
            url: '/keep-token-alive', //http://moredez.ru/q//q/31449434/470749
            type: 'post',
            headers: {
                'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
            }
        }).then(function (result) {
            console.log(new Date() + ' ' + result + ' ' + $('meta[name="csrf-token"]').attr('content'));
        });
    }

});

В javascript, на ваш взгляд:

'keep-token-alive'

Обратите внимание, что вы не должны перечислять VerifyCsrfToken.phpисключения session.php. Как @ ITDesigns.eu подразумеваемого в комментариях, это очень важно для этого маршрута , чтобы убедиться , что есть действительный маркер в настоящее время , и что он просто должно быть его действие продлено.

Почему этот подход решает мою проблему

Мой сайт Laravel позволяет пользователям смотреть видео (час), и он использует ajax для публикации своего прогресса каждую минуту.

Но многие пользователи загружают страницу, а затем не запускают видео до много часов спустя.

Я не знаю, почему они оставляют свою вкладку браузера открытой до того, как смотреть, но они это делают.

И тогда я получу тонны исключений TokenMismatch в своих журналах (и пропустил бы данные о их прогрессе).

В 'lifetime', я изменился web.phpс 120 до 360 минут, но этого все еще было недостаточно. И я не хотел делать это дольше 6 часов. Поэтому мне нужно было включить эту страницу, чтобы часто расширять сеанс с помощью ajax.

Как вы можете проверить это и понять, как работают токены:

В Route::post('refresh-csrf', function() {//Note: as I mentioned in my answer, I think this approach from @UX Labs does not make sense, but I first wanted to design a test view that used buttons to ping different URLs to understand how tokens work. The "return csrf_token();" does not even seem to get used. return csrf_token(); }); Route::post('test-csrf', function() { return 'Token must have been valid.'; }); :

<button id="tryPost">Try posting to db</button>
<button id="getNewToken">Get new token</button>

(function () {
    var $ = require("jquery");

    $(document).ready(function () {
        $('body').prepend('<div>' + new Date() + ' Current token is: ' + $('meta[name="csrf-token"]').attr('content') + '</div>');
        $('#getNewToken').click(function () {
            $.ajax({
                url: '/refresh-csrf',
                type: 'post',
                headers: {
                    'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
                }
            }).then(function (d) {
                $('meta[name="csrf-token"]').attr('content', d);
                $('body').prepend('<div>' + new Date() + ' Refreshed token is: ' + $('meta[name="csrf-token"]').attr('content') + '</div>');
            });
        });
        $('#tryPost').click(function () {
            $.ajax({
                url: '/test-csrf',
                type: 'post',
                headers: {
                    'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
                }
            }).then(function (d) {
                $('body').prepend('<div>' + new Date() + ' Result of test: ' + d + '</div>');
            });
        });


    });
})();

В javascript, на ваш взгляд:

session.php

В 'lifetime', временно меняйте lifetimeчто-то очень короткое для целей тестирования.

Затем поиграйте.

Вот как я узнал, как работает токен Laravel и как нам просто нужно успешно отправлять POST на защищенный CSRF маршрут, чтобы токен оставался действительным.


1

Увеличьте lifetimeсвои сеансы. Вы можете сделать это, отредактировав config/session.phpфайл в вашей конфигурации laravel.

/*
|--------------------------------------------------------------------------
| Session Lifetime
|--------------------------------------------------------------------------
|
| Here you may specify the number of minutes that you wish the session
| to be allowed to remain idle before it expires. If you want them
| to immediately expire on the browser closing, set that option.
|
*/

'lifetime' => 120,

0

Я знаю, что это не лучшее решение, но одно из самых простых. Просто отключите защиту CSRF для этой страницы, где пользователь проводит много времени. Например, на моем сайте они могут писать статью часами на одной странице. И это очень неприятно, если вы не можете сохранить статью из-за защиты CSRF.

PHP, Laravel, Laravel-5,