Подтвердить что ты не робот

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

Каков наилучший способ обработки истекших токенов в laravel 5.

Я имею в виду, что у меня есть страница, и у нее есть ссылки, которые выполняют запросы ajax. Они работают нормально, когда страница загружается, но когда я жду какое-то время, я получаю ошибку TOKEN MISMATCH.

Теперь мне нужно обновить страницу, чтобы она снова работала. НО, я не хочу обновлять страницу. Я хочу, чтобы какой-то способ обновить токен или какую-то другую работу, чтобы он исправился.

Надеюсь, вы поняли мою мысль.

4b9b3361

Ответ 1

работает для него, это фактически получить новый токен в определенное определенное время, иначе вы побеждаете цель токена 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 в течение длительного времени, но, думаю, вы получаете идею

Ответ 2

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

1. Увеличьте время жизни сеанса

//In config/session.php replace this:

'lifetime' => 120

//with:

'lifetime' => 360

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

2. Вызовите исключение и выведете сообщение об ошибке

//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 \Illuminate\Session\TokenMismatchException) {            
        return redirect('/')->withErrors(['token_error' => 'Sorry, your session seems to have expired. Please try again.']);
    }

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

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

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

Ответ 3

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

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

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

В web.php:

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
});

В вашем представлении javascript:

$(document).ready(function () {

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

    function keepTokenAlive() {
        $.ajax({
            url: '/keep-token-alive', //https://stackoverflow.com/q/31449434/470749
            method: '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'));
        });
    }

});

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

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

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

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

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

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

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

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

В web.php:

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

В вашем представлении javascript:

<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',
                method: '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',
                method: 'post',
                headers: {
                    'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
                }
            }).then(function (d) {
                $('body').prepend('<div>' + new Date() + ' Result of test: ' + d + '</div>');
            });
        });


    });
})();

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

Тогда поиграйте.

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

Ответ 4

Лучший способ справиться с этим исключением - использовать App\Exceptions\Handler.php.

public function render($request, Exception $e) {

        if ($e instanceof \Illuminate\Session\TokenMismatchException) {            
            return Redirect::back()->withErrors(['session' => 'Désolé, votre session semble avoir expiré. Veuillez réessayer.']);
        }

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


и где бы вы ни хотели показать это сообщение (на всех своих страницах, которые содержат csrf_token), добавьте этот фрагмент:

<div>
@if(count($errors)>0)
    @foreach($errors->all() as $error)
        <ul>
            <li>{{$error}}</li>
        </ul>
    @endforeach
@endif
</div>

Ответ 5

Увеличьте 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,

Ответ 6

короткий и быстрый способ.... для обработки ajax-запросов, когда срок действия токена истекает: добавьте этот скрипт в конец основного макета или вашего документа

$(window).load(function(){
    $.ajaxSetup({
        statusCode: {
            419: function(){
                    location.reload(); 
                }
        }
    });
});

и для обработки http-запросов по истечении срока действия токена создайте файл 419.blade.php по этому пути:\resources\views\errors и добавьте в него этот скрипт:

<script type="text/javascript">
    //reload on current page
    window.location = '';

</script>

Ответ 7

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

Ответ 8

Я думаю, что лучший вариант - взять конфигурацию жизненного цикла файла config/session.php, а затем значение жизненного цикла, умноженное на 60 * 1000 в коде javascript. Используйте вспомогательную функцию config(), предоставляемую laravel, она может выглядеть так:

<script type="text/javascript">
    var timeout = ({{config('session.lifetime')}} * 60) * 1000;
    setTimeout(function() {
        //reload on current page
        window.location = '';
    }, timeout);
</script>

Ответ 9

Это все обходные пути, которые мне не нравятся.. (но я признаю, что они могут работать). Я не знаю, так как это ведьма версия существует на Laravel, но есть способ исключить страницы из проверки токена CSRF:

https://laravel.com/docs/5.5/csrf

Просто добавьте запись в массив $ кроме массива VerifyCsrfToken Middleware с вашим URI, который вы хотите исключить. Пожалуйста, примите во внимание, это должно быть сделано только в определенных случаях.

Это лучшее решение для меня... Просто и просто (почти) все на Laravel, они уже думали об этом. ;)

Ответ 10

Круговая навигация по токену обычно воспринимается как ужасный подход, но есть проблемы с использованием таймеров js, упомянутых выше. js seetTimeout/setInterval ненадежен, когда вкладка браузера либо не сфокусирована, либо свернута, либо у многих пользователей ноутбук/устройство спит/закрыт и т.д.

Лучшим маршрутом может быть использование таймера js для пересчета "времени смерти" из метки даты, установленной в файле cookie (или мета-тега для суетливых пользователей GDPR без файлов cookie). эта метка даты будет временем реального времени (часового пояса), когда сеанс умрет, и обновляется при каждом обновлении страницы. Таким образом, не имеет значения, что браузер/устройство делали/не делали, пока вас не было И это все равно будет актуально для тех, у кого "держите меня в системе" и т.д.

Следующая проблема заключается в том, что нужно сделать вместо автоматического обновления токена - предоставить пользователю форму для повторного входа в систему (модал/всплывающее окно), которая перенаправляет новый токен на страницу, как упомянуто выше.

Ответ 11

Вы можете попробовать пакет Caffeine for Laravel, установить интервал, затем обновить токен, как предложено в некоторых ответах, и он будет добавлен автоматически в каждую форму, имеющую токен csrf.