Многостраничная структура Laravel JWT

Насколько мне известно, система авторизации на основе JWT обычно зарезервирована для SPA (вы знаете, одно представление, одно приложение React / Angular / Vue с одним раздутым файлом app.js), однако я пытаюсь использовать магию JWT с немного отдельным структурированным приложением.

Состав

Вместо того, чтобы обслуживать одно представление blade.php из моего приложения Laravel, которое объединяет одно приложение и экземпляр Vue, я пытаюсь обслужить ДВА отдельных представления blade.php, каждое из которых работает как свое собственное отдельное представление Vue SPA: для внешней стороны приложения (предварительная проверка ) и еще один для внутренней части приложения (после авторизации).

Текущее состояние приложения

Для обеспечения работы системы аутентификации моего приложения я использовал библиотеку Jwt-auth Таймона (красивая библиотека кстати) и связал все вместе на лицевой стороне с помощью (как говорилось ранее) Vue/Vuex. Все работает так, как ожидалось, в моих компонентах Register и Login я могу попасть в свой api, получить JWT в ответ, сохранить его локально, а затем добавить указанный токен в мои заголовки Axios, позволяя всем последующим запросам содержать этот токен.

Дилемма

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

Route::get('/home', 'Auth\HomeController@home')->middleware('jwt');

промежуточное ПО

class JWT
{
    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return mixed
     */
    public function handle($request, Closure $next)
    {
        JWTAuth::parseToken()->authenticate();

        return $next($request);
    }
}

и мой просмотр до авторизации и все его маршруты защищены встроенным гостевым промежуточным программным обеспечением Laravel RedirectIfAuthenticated, которое теперь охраняется JWT:

class RedirectIfAuthenticated
{
    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @param  string|null  $guard
     * @return mixed
     */
    public function handle($request, Closure $next, $guard = null)
    {
        if (Auth::guard($guard)->check()) {
            return redirect('/home');
        }

        return $next($request);
    }
}

Вопросов

Отсюда возникают следующие вопросы:

1) после успешной регистрации / входа в интерфейс и JWT сгенерирован, сохранен локально и в заголовках Axios, как мне затем перенаправить на мой маршрут после авторизации с этим доступным действующим токеном?

2) Как мне затем убедиться, что действительный JWT сохраняется и присутствует при попадании в гостевые маршруты для успешного перенаправления обратно на мой маршрут после авторизации?

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

Оба эти приложения находятся в одном домене или в разных доменах?

Lassi Uosukainen 22.03.2018 16:50

Тот же домен, установка / проект Laravel по умолчанию с Vue на интерфейсе @Lassi Uosukaimen

Jared Garcia 22.03.2018 17:07

Итак, проблема в том, что у вас нет доступного токена после перенаправления на другую страницу? Или действительно просто обновляете ту же страницу?

tobbr 22.03.2018 17:15

Точнее, токен генерируется на бэкэнде и отправляется во внешний интерфейс для локального хранения (в хранилище браузера) и добавляется в заголовки axios, чтобы каждый последующий запрос ajax отправлялся с токеном @tobbr

Jared Garcia 22.03.2018 17:26

Храните токен в файлах cookie, а не в локальном хранилище. Безопасность и т. д. Установите тайм-аут для JWT в соответствии с тайм-аутом для сеанса Laravel. Проверяйте, истек ли токен в промежуточном программном обеспечении для каждого запроса, если он истекает, перенаправьте их на страницу входа. Отправляйте токен-носитель из файла cookie с каждым последующим запросом axios.

Ohgodwhy 22.03.2018 17:52

есть ли способ хоть немного, удаленно, отчетливо показать это в коде? :} @О, Боже, почему

Jared Garcia 22.03.2018 18:08

Я хочу, чтобы в каждом запросе был токен, даже от другого контроллера, как я могу это сделать?

Ali Raza 16.09.2019 16:07
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Symfony Station Communiqué - 7 июля 2023 г
Symfony Station Communiqué - 7 июля 2023 г
Это коммюнике первоначально появилось на Symfony Station .
Оживление вашего приложения Laravel: Понимание режима обслуживания
Оживление вашего приложения Laravel: Понимание режима обслуживания
Здравствуйте, разработчики! В сегодняшней статье мы рассмотрим важный аспект управления приложениями, который часто упускается из виду в суете...
Установка и настройка Nginx и PHP на Ubuntu-сервере
Установка и настройка Nginx и PHP на Ubuntu-сервере
В этот раз я сделаю руководство по установке и настройке nginx и php на Ubuntu OS.
Коллекции в Laravel более простым способом
Коллекции в Laravel более простым способом
Привет, читатели, сегодня мы узнаем о коллекциях. В Laravel коллекции - это способ манипулировать массивами и играть с массивами данных. Благодаря...
Как установить PHP на Mac
Как установить PHP на Mac
PHP - это популярный язык программирования, который используется для разработки веб-приложений. Если вы используете Mac и хотите разрабатывать...
6
7
1 590
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Итак, есть несколько способов убедиться, что токен JWT доступен везде для Axios или для любого внешнего интерфейса.

Наиболее распространенный способ - сохранить токен либо в печенье, либо в веб-хранилище браузера (localStorage / sessionStorage).

Разница между localStorage и sessionStorage заключается в том, что данные, хранящиеся в localStorage, сохраняются во время сеансов браузера, sessionStorage очищается при завершении сеанса страницы.

По общему мнению, файлы cookie немного более безопасны, поскольку имеют меньший вектор атаки, хотя ни один из методов не является полностью безопасным. Если вы хотите углубиться, вы можете начать с чтения статьи это.

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

Теперь, когда у вас есть токен на каждой странице, вы можете перенаправить пользователя любым способом. Хотя я бы посоветовал вместо использования вашего собственного промежуточного программного обеспечения для аутентификации JWT вы можете использовать то, которое предоставляет библиотека JWT: jwt.auth.

Это промежуточное ПО будет автоматически отвечать кодами ошибок, если с токеном что-то не так, и если есть, оно вернет один из следующих HTTP-ответов:

  • token_not_provided
  • token_expired
  • token_invalid
  • user_not_found

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

При входе в систему после сохранения токена в cookie используйте интерфейс для перенаправления на маршруты после авторизации.

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

ОБНОВИТЬ

Очень простой пример того, как вы можете пройти аутентификацию только с охранником и при этом получить токен для API. Заимствуя пример перенаправления из @Ohgodwhy, вы можете поместить следующее в промежуточное ПО RedirectIfAuthenticated.

public function handle($request, Closure $next, $guard = null)
    if (Auth::guard($guard)->check()) {
        if ((\Cookie::get('access_token') == null)) {
            $cookie = \Cookie::make(
                'access_token',
                \JWTAuth::fromUser(Auth::user()),
                config('session.lifetime'),
                null,
                $request->refeerer,
                false, // to make the cookie available in javascript
                false // to make the cookie available in javascript
            );

            return redirect('/home')->cookie($cookie);
        } else {
            return redirect('/home');
        }
    }

    return $next($request);
}

Просто убедитесь, что ваш $redirectTo в app/Http/Controllers/Auth/LoginController.php настроен на путь, реализующий промежуточное ПО RedirectIfAuthenticated.

я имею в виду, не могу ли я? в моих методах register или login в моем бэкэнде, после успешной аутентификации и генерации токена, не могу ли я просто вызвать перенаправление на мой маршрут post-auth, добавив в настраиваемый заголовок, значением которого является токен?

Jared Garcia 22.03.2018 18:42

Насколько мне известно, вместо того, чтобы возвращать cookie в ответ, вы можете использовать подход @Ohgodwhy, описанный для установки cookie из бэкэнда, тогда вам не нужно будет его возвращать.

tobbr 22.03.2018 19:05

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

Jared Garcia 22.03.2018 19:07

Ну, в таком случае, почему бы просто не изменить промежуточное ПО RedirectIfAuthenticated для перенаправления на ваши предварительные маршруты, если проверка защиты не удалась?

tobbr 22.03.2018 19:22

Чтобы уточнить, что я имею в виду, это использование встроенной защиты Laravels для обработки доступа к представлениям /post-auth и /pre-auth и использование JWT для обработки доступа к API.

tobbr 22.03.2018 19:31

ну, потому что для этого будут использоваться два устройства защиты аутентификации, а это, я считаю, довольно неэффективно. Я буду использовать встроенную защиту laravel для аутентификации, а затем, когда в post auth для доступа к любым маршрутам API, мне придется снова аутентифицироваться с JWT для выполнения последующих запросов ...

Jared Garcia 22.03.2018 19:36

Не совсем так, поскольку вы можете использовать $token = JWTAuth::fromUser(Auth::user());, чтобы получить токен для пользователя, который прошел аутентификацию с помощью охранника. Это то, что JWT делает в фоновом режиме. Но если вы не хотите делать это таким образом, я полагаю, вам нужно сделать JWTAuth::setToken($request->cookie('access_token'))->authent‌​icate(); в промежуточном программном обеспечении jwt вместо того, чтобы разбирать его из запроса.

tobbr 22.03.2018 19:53

если вы можете вкратце продемонстрировать логику аутентификации, реализующую встроенную защиту Laravel сначала, чтобы перейти от представления до авторизации к представлению после авторизации, а затем используя JWT до конца после авторизации (с его SPA), это было бы потрясающе

Jared Garcia 22.03.2018 20:03

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

Jared Garcia 22.03.2018 21:27

Я хочу, чтобы в каждом запросе был токен, даже от другого контроллера, как я могу это сделать?

Ali Raza 16.09.2019 15:06

При успешном входе в систему у вас будет токен, скажем, он называется $jwt_token.

Вы можете перенаправить на защищаемую страницу после авторизации и установить cookie в ответе:

return redirect('/home')->cookie(
    'access_token', //name
    $jwt_token,  //value
    config('session.lifetime'), //expiration in minutes (matches laravel)
    config('app.url'),  // your app url
    true // HttpsOnly
);

Отсюда Axios может получить доступ к cookie путем анализа файлов cookie в документе и получения access_token.

let token = document.cookie.split(';') // get all your cookies
    .find(cookie => cookie.includes('access_token')) // take only the one that matches our access_token name
    .split('=')[1] // get just the value after =

// terrible code example above for you

Теперь вы можете использовать это в своих запросах Axios, добавив его в качестве значения для Bearer в заголовке Authorization:

Authorization: `Bearer ${token}`

Ваше промежуточное ПО JWT уже использует метод authenticate, и поэтому оно должно обрабатывать expiry в его нынешнем виде:

JWTAuth::parseToken()->authenticate();

Под капотом это попытается проверить срок действия токена на основе текущего TTL, установленного в файле config/jwt.php. Учитывая ваш рабочий процесс, я бы также blacklist токен, если он истекает. Вы можете добавить Event Listener, который прослушивает просроченные токены и помещает их в черный список, прослушивая Event::listen('tymon.jwt.expired');.

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

да, ваш подход к перенаправлению после входа в систему соответствовал тому, о чем я думал, однако промежуточное ПО JWT требует, чтобы токен был отправлен вместе с запросами на любой маршрут, который он защищает, поэтому, если я не могу перенаправить на /home при установке токена в заголовок или отправка токена каким-то образом, я никогда не достигну /home, просто перенаправляю обратно

Jared Garcia 22.03.2018 18:40

Я хочу, чтобы в каждом запросе был токен, даже от другого контроллера, как я могу это сделать?

Ali Raza 16.09.2019 15:06
Ответ принят как подходящий

Итак, вот логика, которую я реализовал:

В моей функции входа в систему LoginController.php после успешной аутентификации и генерации JWT я возвращаю ответ с Json и новым файлом cookie, оба с переданным новым token:

public function login(Request $request)
{
    $creds = $request->only(['email', 'password']);

    if (!$token = auth()->attempt($creds)) {
        return response()->json([
            'errors' => [
                'root' => 'Incorrect Credentials. Try again'
            ]
        ], 401);
    }

    return $this->respondWithToken($token);
}

protected function respondWithToken($token)
{
    return response()->json([
        'meta' => [
            'access_token' => $token,
            'token_type' => 'bearer',
            'expires_in' => auth()->factory()->getTTL() * 60
        ]
    ], 200)
    ->withCookie(cookie('access_token', $token, auth()->factory()->getTTL()));
}

В моем гостевом промежуточном программном обеспечении RedirectIfAuthenticated проверяется наличие cookie, если он существует, setToken, который, в свою очередь, устанавливает Guard в режим аутентификации и всегда будет перенаправлять на /home, если токен доступен и действителен:

public function handle($request, Closure $next, $guard = null)
{
    if ($request->hasCookie('access_token')) {
        Auth::setToken($request->cookie('access_token'));
    }

    if (Auth::guard($guard)->check()) {
        return redirect('/home');
    }

    return $next($request);
}

И в моем промежуточном программном обеспечении post-auth Routes я также setToken, и, если он действителен и существует, разрешит доступ, в противном случае будет выдан ряд ошибок JWT, которые просто перенаправляют на просмотр до авторизации:

public function handle($request, Closure $next)
{
    JWTAuth::setToken($request->cookie('access_token'))->authenticate();

    return $next($request);
}

Наконец, я решил обработать перенаправление во внешнем интерфейсе, так как я использую Axios, который, как обещано, основан и может гарантировать, что cookie будет установлен перед перенаправлением в режим пост-авторизации, поэтому никаких забавных дел не происходит! Ваше здоровье! Надеюсь, это поможет любому на пути к магии многостраничного СПА!

Другие вопросы по теме