programing

Laravel에서 만료된 토큰 처리 중

javaba 2023. 8. 17. 23:20
반응형

Laravel에서 만료된 토큰 처리 중

라벨 5에서 만료된 토큰을 처리하는 가장 좋은 방법은 무엇입니까?

내 말은 내가 페이지를 가지고 있고 그것은 아약스 요청을 수행하는 몇 개의 링크를 가지고 있다는 것입니다.페이지가 로드되면 정상적으로 작동하지만, 잠시 기다리다가 토큰 불일치 오류가 발생합니다.

이제 페이지가 다시 작동하도록 새로 고쳐야 합니다.그러나 페이지를 새로 고치고 싶지 않습니다.토큰을 새로 고칠 수 있는 방법이나 수정할 수 있는 다른 방법이 필요합니다.

제 요점을 이해하셨기를 바랍니다.

@UX Labs의 답변은 오해의 소지가 있다고 생각합니다.그리고 @jfadich의 코멘트는 완전히 잘못된 것 같습니다.

2017년 5월 Laravel 5.4의 경우 다음과 같이 문제를 해결했습니다.

효과적인 해결책이 있습니다.

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.phpAs @ITDesigns.eu 은 논평에서 이 경로에 현재 유효한 토큰이 있는지, 만료 기간만 연장하면 되는지 확인하는 것이 중요하다고 암시했습니다.

이 접근 방식이 문제를 해결하는 이유

나의 라라벨 사이트는 사용자들이 비디오를 볼 수 있게 해주고(1시간 길이), 아약스를 사용하여 매 분마다 진행 상황을 게시합니다.

그러나 많은 사용자가 페이지를 로드한 다음 몇 시간이 지나야 비디오를 시작합니다.

왜 그들이 보기 전에 브라우저 탭을 그렇게 오래 열어두는지 모르겠지만, 그들은 그렇게 합니다.

그런 다음 로그에 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 토큰이 어떻게 작동하는지 그리고 토큰이 계속 유효하도록 하기 위해 CSRF로 보호되는 경로에 성공적으로 자주 게시해야 하는 방법을 배운 방법입니다.

2022년 업데이트;csrf_token()메소드는 토큰을 만들지 않으며 단순히 현재 세션에서 기존 CSRF 토큰을 로드하고 반환합니다.

하지만 이것은 효과가 있다고 생각하도록 속입니다. 왜냐하면 Laravel은 기존 CSRF 토큰의 수명을 늘리고 CSRF로 보호되는 경로에 대한 요청이 이루어질 때마다 증가하기 때문입니다.

새로운 CSRF 토큰을 실제로 생성하는 구현은 다음을 참조하십시오.

stackoverflow.com/Get 아약스와 함께하는 새로운 CSRF 토큰?

원답

해결 방법은 특정 시간마다 실제로 새 토큰을 얻는 것입니다. 그렇지 않으면 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>

광활한 경로로

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

구문 오류가 발생한 경우 죄송합니다. 오랫동안 jquery를 사용하지 않았습니다. 하지만 당신은 이해할 수 있을 것 같습니다.

나는 이 경우를 위해 두 가지를 결합합니다.

세션 수명 연장

//In config/session.php replace this:

'lifetime' => 120

//with:

'lifetime' => 360

Larvel 5 기본 수명은 120(분)입니다. 원하는 값으로 변경할 수 있습니다(예: 360(6시간).

예외를 감지하고 오류 메시지 표시

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

문서에 따르면:

Laravel은 응용프로그램에서 관리하는 각 활성 사용자 세션에 대해 CSRF "토큰"을 자동으로 생성합니다.

즉, 사용자가 방문하는 모든 페이지에 대해 csrf 코드가 동일하다는 것을 의미합니다.세션이 만료되면 유효하지 않습니다.따라서 수명을 1주일로 설정하면 CSRF 토큰은 1주일 후에만 만료됩니다.

이는 다음과 같이 달성할 수 있습니다.config/session.php:

 /*
    |--------------------------------------------------------------------------
    | 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' => 60 * 24 * 7, // Set session lifetime to 1 week

    'expire_on_close' => true,

위의 답변 중 하나라도 마음에 들지 않는 이유:

  1. UX Labs의 답변:

상태로 하고 새 합니다.token연 경우 .사용자가 탭을 여러 개 연 경우 이 문제가 발생합니다.을 한 번 새로 .CSRF토큰, 다른 모든 탭은 유효하지 않습니다.

  1. 라이언의 답변

CSRF를 변경하지 않기 때문에 더 좋습니다.token여러 탭은 영향을 받지 않습니다.그것은 단지 일정한 시간 후에 js 전화를 걸면서 세션을 활성화합니다.setInterval .만지하,setIntervalPC가 절전 모드로 전환되는 동안에는 작동하지 않습니다.따라서 PC가 절전 모드로 전환되면 세션이 만료될 수 있으며, 이 또한 가능한 시나리오입니다.따라서 js-calls로 세션을 유지하는 대신 수명을 늘립니다.

  1. 폴레알렉산드루의 답변

세션이 시간 초과되었을 때 오류를 표시하는 것은 괜찮지만 문제가 발생하지 않는 것이 좋습니다.수명을 6시간으로 설정하는 것만으로는 충분하지 않습니다. 탭이 며칠 동안 열려 있을 수 있기 때문입니다.

  1. 기타 답변

다른 모든 답변은 문제의 경로에 대해 CSRF를 비활성화할 것을 제안하지만, 이는 물론 큰 보안 위험을 초래하기 때문에 옵션이 아닙니다.

하는 가장 은 다음과 같습니다.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>

를 .lifetime당신의 세션의.다음을 편집하여 이 작업을 수행할 수 있습니다.config/session.php파일을 저장할 수 있습니다.

/*
|--------------------------------------------------------------------------
| 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,

다음과 같은 간단한 솔루션이 있습니다.

  • 세션 수명을 연장할 필요가 없습니다.
  • 여러 탭을 연 상태에서 작동합니다.
  • 장치가 꺼져 있어 세션이 시간 초과된 경우에도 작동합니다.

/dll/web.dll:

$router->get('csrf-token', function() {
   return request()->session()->token();
});

이렇게 하면 현재 csrf 토큰이 반환됩니다.

  • 이 경로가 호출될 때까지 토큰이 유효하지 않은 경우(예: 장치가 장시간 꺼져 있는 경우) 세션을 시작하여 생성된 새 토큰이 반환됩니다.
  • 유효한 토큰이 남아 있는 경우 반환됩니다.이 경로를 호출하면 세션이 연장되므로 토큰 수명도 연장됩니다.

필요한 경우에만 새 토큰을 반환하기 때문에 @Adam이 설명한 대로 여러 탭을 열어도 문제가 없습니다.

서 X는 수명 - ._token입력 ▁i 과 같이 를 사용합니다).이 작업을 다음과 같이 수행합니다(여기서는 momentjs와 axios를 사용합니다).

handleNewCsrfToken();

// Use visbility API to make sure the token gets updated in time, even when the device went to sleep.
document.addEventListener('visibilitychange', function() {
    if (document.visibilityState === 'visible') {
        setTimeoutToRefreshCsrfToken();
    } else if (document.visibilityState === 'hidden') {
        clearTimeout(refreshCsrfTokenTimeout);
    }
});

function handleNewCsrfToken() {
    updateCsrfTokenTimeoutTarget();
    setTimeoutToRefreshCsrfToken();
}

function updateCsrfTokenTimeoutTarget() {
    csrfTokenTimeoutTarget = moment().add(2, 'hour').subtract(5, 'minute');
}

function setTimeoutToRefreshCsrfToken() {
    refreshCsrfTokenTimeout = setTimeout(refreshCsrfToken, csrfTokenTimeoutTarget.diff());
}

function refreshCsrfToken() {
    axios.get('/csrf-token').then(function(response) {
        document.getElementsByName('_token').forEach(function(element) {
            element.value = response.data;

            handleNewCsrfToken();
        });
    });
}

당신의 메인 레이아웃 파일에서 이것을 시도해 보세요.

@guest
    <meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" />
    <meta http-equiv="Pragma" content="no-cache" />
    <meta http-equiv="Expires" content="0" />
    <meta http-equiv="refresh" content="{{config('session.lifetime') * 60}}">
@endguest

짧고 빠른 길...토큰 만료 시 Ajax 요청 처리: 마스터 레이아웃 또는 문서의 끝에 이 스크립트 추가

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

토큰이 만료될 때 http 요청을 처리하려면 419.blade를 생성합니다.php 경로: \beta\beta\beta\beta에서 이 스크립트를 추가합니다.

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

</script>

토큰을 우회하는 것은 일반적으로 끔찍한 접근 방식으로 받아들여지지만 위에서 언급한 jsimmer를 사용하는 데에도 문제가 있습니다. jsseetTimeout/set브라우저 탭이 초점이 맞지 않거나, 최소화되지 않았거나, 많은 사용자의 경우 랩톱/장치가 절전/닫혀 있는 등의 경우 간격이 불안정합니다.

더 나은 경로는 쿠키(또는 까다로운 GDPR no-cookie 사용자를 위한 메타 태그)에 설정된 날짜 스탬프에서 '죽는 시간'을 재계산하기 위해 js 타이머를 사용하는 것일 수 있습니다.이 날짜 스탬프는 세션이 종료되는 실제(시간대) 시간이 되며 페이지를 새로 고칠 때마다 업데이트됩니다.이렇게 하면 사용자가 부재 중일 때 브라우저/장치가 무엇을 하고 있었는지/하지 않았는지는 중요하지 않으며, '로그인 상태 유지' 등을 사용하는 사용자에게는 정확합니다.

다음 문제는 토큰을 자동으로 새로 고치는 대신 수행할 작업입니다. 위에서 언급한 대로 새 토큰을 페이지에 아약스하는 '재로그인' 양식(모달/팝업)을 사용자에게 제공합니다.

당신은 라라벨 패키지를 위해 카페인을 시도할 수 있습니다. 간격을 설정한 후 일부 답변에서 제안된 것처럼 토큰을 새로 고칩니다. 또한 csrf 토큰을 가진 모든 형식에서 자동으로 추가됩니다.

가장 좋은 방법은 config/session.php 파일의 라이프타임 구성을 취한 다음, 라이프타임 값에 자바스크립트 코드의 60 * 1000을 곱하는 것입니다.라벨에서 제공하는 도우미 기능 구성()을 사용하면 다음과 같이 보일 수 있습니다.

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

이것들은 모두 제가 좋아하지 않는 해결책들입니다.(하지만 작동할 수 있다는 것은 인정합니다) 스위치 버전이 라라벨에 존재하기 때문에 잘 모르겠지만 CSRF 토큰 검증에서 페이지를 제외하는 방법이 있습니다.

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

제외할 uri를 사용하여 VerifyCsrfToken 미들웨어의 $except 배열에 레코드를 추가하기만 하면 됩니다.이 작업은 특정한 경우에만 수행해야 합니다.

이것이 나에게 가장 좋은 해결책입니다.단순하고 (거의) 라라벨의 모든 것과 마찬가지로, 그들은 이미 그것에 대해 생각했습니다. ;)

언급URL : https://stackoverflow.com/questions/31449434/handling-expired-token-in-laravel

반응형