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

Сессии в аутентификации на токенах

Я создаю приложение в PHP Lumen, которое возвращает токен при входе в систему. Я не уверен, как действовать дальше этого.

Как я должен поддерживать сеанс, используя эти токены?

В частности, как я могу хранить токены на стороне клиента, если я использую responsejs или ванильный HTML/CSS/jQuery и отправляю их в каждом запросе, который я делаю для защищенной части моего веб-приложения?

4b9b3361

Ответ 1

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

localStorage.setItem('app-token', theTokenFromServer);

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

token = localStorage.getItem('app-token');

Если вы используете реакцию, я бы сохранил токен в глобальном состоянии (например, с помощью сокращения):

function loadAppToken(token) {
  return {
    type: 'LOAD_TOKEN',
    payload: { token },
  };
}

С vanilla javascript я бы сохранил его в моей утилите подключения. Что может выглядеть примерно так:

const token = localStorage.getItem('app-token');

export function request(config) {
   const { url, ...others } = config;

   return fetch(url, {
     ...others,
     credentials: 'include',
     headers: {
       'Authorization': `Bearer ${token}`
     },
   });
}

У меня все еще есть утилита fetch в приложении для реагирования, похожая на предыдущий код, но я бы послал токен в параметрах, получив его в промежуточном программном обеспечении redux для каждого отдельного запроса.

Ответ 2

В настоящее время работает над тем же типом приложения, используя lumen для API. После трех шагов аутентификации на основе токена в Lumen с JWT:

1. Создать токен и вернуться после успешного входа в систему

public function login(Request $request) {
    $token = $this->jwt->attempt(['user_name' => $data['user_name'], 'password' => $data['password']]); //$token = $this->jwt->attempt($data); 
    if (!$token) {
        $response = array('success' => false, 'data' => null, 'detail' => array('message' => Messages::MSG_INVALID_USER, 'error' => array(Messages::MSG_INVALID_USER)));
        return response()->json($response);
    } else {
        $user = \Auth::setToken($token)->user();
        $data = array('token' => $token,'user_id' => $user->id);
        $response = array('success' => true, 'data' => $data, 'detail' => array('message' => Messages::MSG_SUCCESS, 'error' => null));
        return response()->json($response);
    }
}

2. Определение промежуточного программного обеспечения для проверки токена

public function handle($request, Closure $next, $guard = null) {
    try {
        $token = $request->header('X-TOKEN');
        $user_id = $request->header('X-USER');
        $user = \Auth::setToken($token)->user();
        if ($user && $user->id == $user_id) {
            return $next($request);
        } else {
            $response = array('success' => false, 'data' => null, 'detail' => array('message' => Messages::MSG_ERR_INVALID_TOKEN, 'error' => Messages::MSG_ERR_INVALID_TOKEN));
            return response()->json($response);
        }
    } catch (Exception $ex) {
        $response = array('success' => false, 'data' => null, 'detail' => array('message' => Messages::MSG_ERROR_500, 'error' => array($ex)));
        return response()->json($response);
    }
}

3. Сохранить токен в localstorage или в файлах cookie

localStorage.setItem("Token", JSON.stringify(TokenData));
TokenData = JSON.parse(localStorage.getItem("Token"));

или

$.cookie('Token', JSON.stringify(TokenData), {expires: 1, path: '/'});
TokenData = JSON.parse($.cookie("Token"));

4. Отправить токен с каждым запросом в заголовках

Запрос с настраиваемыми заголовками

$.ajax({
    url: 'foo/bar',
    headers: { 'X-TOKEN': TokenData.Token ,'X-USER': TokenData.UserId}
});

Заголовки для каждого запроса

$.ajaxSetup({
        headers: { 'X-TOKEN': TokenData.Token ,'X-USER': TokenData.UserId}
    });

Надеюсь, что это поможет.

Примечание. Добавьте некоторые проверки и проверки данных при чтении данных с localstorage или cookies.

Ответ 3

Предположим, вы хотите построить APP с.

  • ReactJS
  • API REST с PHP
  • Использование JWT

1. Введение

Вы должны забыть о сеансах при создании API REST.

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

2. Аутентификация

Все, что клиент хочет сделать, это только обмен некоторыми username и password для токена.

Это пример HTTP-запроса

POST /api/v1/authentication HTTP/1.1
Host: localhost
Content-Type: application/json
{
    "username": "foo",
    "password": "bar"
}

И ответ:

{
    "token": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
}

3. более подробно рассмотрим запрос/ответ

Как наш API будет обрабатывать запрос аутентификации?

  • Он будет проверять, существует ли пользователь с именем пользователя foo и паролем bar и он активен в DB

  • Он будет генерировать JWT (Json Web Token)

  • Он вернет ответ, содержащий JWT

Это простой супер-метод auth, например.

public function authAction()
{
  /** Get your payload somehow */
  $request = $_POST;

  //Validate if username & password are given/

  $user = $this->model->auth($username, $password);

  if(!$user) {
    //throw error for not valid credentials
  }

  $jwt = $this->jwt->create($user);

  //return response with $jwt
}

Как вы видите, это не сеансы, установленные или что-то еще.

Как наша клиентская сторона обработает ответ?

Клиент может использовать какой-то пакет, например superagent для обработки запросов и ответов на наш API таким образом, процесс будет упрощен до это:

  let data = {
    username: email,
    password: password
  };

  request
    .post('/api/v1/authentication')
    .set('Content-Type', 'application/json')
    .send(data)
    .end(function (error, response) {
      //response.body.token
    });

4. Создание JWT на стороне сервера

Вы можете использовать некоторый пакет 3RD PT для генерации и проверки JWT вместо того, чтобы писать его самостоятельно.

Посмотрите на этот пакет, вы можете увидеть, как это делается.

И не забудьте всегда создавать сильные подписи. Я рекомендую использовать RSA keys

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

5. Сохранение JWT на стороне клиента

Это два способа, которые вы уже знаете localStorage и cookies Для меня я использую файлы cookie, потому что:

  • Они немного защищены .
  • Дата истечения срока действия может быть установлена ​​без реализации какой-либо пользовательской логики.
  • Старая поддержка браузера (очень старые браузеры, поэтому это не так важно).

Но все зависит от вас.

6. Использование JWT

Отныне каждый запрос на сервер вы должны включить свой JWT.

В вашем REST API вы должны написать метод для проверки JWT и обмена его для пользовательского объекта.

Пример запроса:

  let jwt = ...; //GET IT FROM LOCALSTORAGE OR COOKIE

  request
    .get('/api/v1/posts')
    .set('Content-Type', 'application/json')
    .set('Authorization', jwt)
    .end(function (error, response) {

    });

Как API обработает этот запрос

public function postsAction()
{
  $jwt = $this->headers->get('Authorization');

  if(!$this->jwt->validate($jwt)) {
    //throw unauthorized error
  }

  $user = $this->model->exchangeJWT($jwt);

  //Your logic here
}

7. Дата истечения срока действия и файл cookie

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

Дата истечения срока действия cookie должна быть равна дате истечения JWT.

Ответ 4

Вы можете сохранить его в браузере localStorage, а затем установить его в заголовке для каждого запроса на сервер.

Ответ 5

Для шифрования и дешифрования вы можете использовать встроенную laravel Crypt Model

использовать Illuminate\Support\Facades\Crypt;

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

Позвольте создать данные

$data = [
    'user_id' => $user->id,
    'time_stemp' => \Carbon::now() // Carbon is laravel time model(class) for managing times
    'expire_on' => \Carbon::now()->addDays(2); //here i'm setting token expires time for 2 days you can change any
];

$data = serialize($data);

затем зашифруйте свои данные с помощью Crypt

$accessToken = Crypt::encrypt($data);

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

Теперь в каждом запросе передайте этот токен и на стороне сервера создайте одно промежуточное изделие, которое будет анализировать ваши данные, и если время вашего токена меньше времени истечения времени, а затем переместите еще раз, отправьте ошибку 403 или что угодно.

Как анализировать данные на стороне сервера

Создать промежуточное программное обеспечение с помощью команды: php artisan make: middleware ApiAuth, тогда это часть дескриптора

//Accesstoken you passed in $headers or in $request param use whatever you like
$searilizerData = Crypt::decrypt($headers['AccessToken']);
$data = unserialize($searilizerData);
//check if expire_on is less then current server time
if($data['expire_on] <= \Curbon::now()){
   next(); // let them contuine and access data
} else {
      throw new Exception ("Your token has expired please regenerate your token",403);
}

Надеюсь, это поможет:)

Ответ 6

Вам действительно не нужны никакие ReactJS или VanillaJS. Просто чистый HTML и PHP на самом деле. Я просто храню его как файл cookie.

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

setcookie('userid',$userid, time()+(3600 * 24 * 15),"/");
setcookie('accesstoken',$accesstoken, time()+(3600 * 24 * 15),"/");
header('Location: /home.php');
//You can change the 15 in setcookie() to amount of days the cookie will expire in.
//The "/" in setcookie is important, because it ensures the cookies will be available on every page the user visits on your website.
//The header function redirects to your home page after log in

Затем ниже будет выглядеть ваша домашняя страница. Он проверяет, существует ли accesstoken cookie, если он это делает, он дважды проверяет, соответствует ли токен текущему токену в пользовательской базе данных. Если это совпадение, отображается страница входа в систему. Если нет, вы должны показать/перенаправить на страницу входа.

<?php
if (isset($_COOKIE['accesstoken']))
{
//connect to your user database and check that the cookie accesstoken matches
// if it doesn't match, deal with it appropriately, such as deleting all cookies then redirecting to login page.
}
?>
<!DOCTYPE HTML>
<html>
<head>
<title>Sup</title>
</head>
<body>
<?php if (isset($_COOKIE['accesstoken'])){ ?>

<h1>User logged in!</h1>
<h3>Do whatever you need to do if user is logged in</h3>

<?php } else { ?>

<h1>No accesstoken found</h1>
<h3>More than likely you will want to show login page here</h3>

<?php } ?>
</body>
</html>

а затем выйти из системы просто. В приведенном ниже коде удаляются accesstokens, устаревшие их:

setcookie("accesstoken", "", time() - 3600);
setcookie("userid", "", time() - 3600);
header('Location: /youareloggedout.html');

Помните, что это ОСНОВА функциональной системы входа/выхода из системы. Если я объясню все необходимые меры безопасности, этот пост будет еще длиннее. Обязательно сделайте свое исследование. Некоторые темы, чтобы начать вас, являются подготовленными заявлениями и предотвращают атаки XSS.:)

Ответ 7

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

Backend

  • (POST) логин для входа {email, password} он создаст токен. Вы можете использовать JWT (Json Web Token) Токен будет возвращен клиенту. Внутри токена вы можете сохранить некоторые основные сведения: идентификатор пользователя, имя пользователя, срок действия токена, тип пользователя и т.д. https://jwt.io/

Client

  • запрос на вход, перейдите {email, password}.

    При успехе, получите токен и сохраните его локально, предпочтительнее localstorage, но cookie также возможен.

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

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

  • logout, довольно просто... просто удалите токен с клиентской стороны и перейдите на страницу входа.

  • Убедитесь, что для "аутентифицированных" страниц вы проверяете наличие токена, и еще больше можете проверить тип пользователя.

** для декодирования на стороне клиента JWT, вы можете использовать: https://www.npmjs.com/package/jwt-client

Ответ 8

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

  • После входа в систему, отправка учетных данных пользователя для входа в API. После успеха верните токен из back-end API. Back-end поддерживает создание и срок действия маркера.
  • Храните токен в состоянии реакции (мы используем хранилище redux) и в хранилище сеансов (в случае, если страница обновлена, мы можем вернуть ее из хранилища сеансов).
  • (необязательно) Запустите счетчик в секунду в хранилище сеансов (чтобы проверить, как долго пользователь не работает)
  • После входа в систему каждый вызов API требует, чтобы токен был отправлен в заголовке. Вызов API выполняется с помощью fetch. Если вызов API будет успешным, мы получим маркер обратно из back-end, и мы заменим его существующим токеном (останемся свежим).
  • Все вызовы API 'fetch'ed через общую функцию customFetch. Идея состоит в том, чтобы иметь общую выборку, чтобы увидеть, соответствует ли back-end ответ 401 (доступ запрещен). Если это 401, токен истек или недействителен (пользователь пытается получить доступ к чему-либо без входа в систему). В этом случае мы выгружаем пользователя из портала, обратно на логин/домашнюю страницу (отображаем ошибку, доступ которой запрещен).
  • (необязательно). Если пользователь слишком долго работает (проверка второго счетчикa > 900, т.е. 15 минут), мы показываем пользователю предупреждение о завершении сеанса, дает пользователю возможность продолжить, Если пользователь нажимает кнопку "Продолжить", мы вызываем API для повторного получения профиля пользователя, поэтому убедитесь, что токен все еще действителен. Если API не удался, мы выходим из системы и отправляем обратно на логин/домашнюю страницу. Второй счетчик возвращается к 1 непосредственно перед вызовом API (пользователь активен и что-то делает).
  • Излишне говорить, что перед отправкой пользователя на логин/домашнюю страницу по любому из вышеуказанных сценариев мы очищаем хранилище сеансов и reset состояние (хранилище редуктов).
  • В случае какого-либо обновления происходит извлечение токена из хранилища сеансов и отправка начальных действий для восстановления состояния (хранилище редукции). Если какое-либо из действий (API) терпит неудачу, мы выводим сообщение пользователю о том, что сессия истекла или недействительна, и вам необходимо войти в систему, отправив пользователя обратно на логин/домашнюю страницу.

Фрагменты кода

Предположим, что вы извлекли токен из вызова API входа:

установить токен в хранилище сеансов и состояние (хранилище редуктов)

window.sessionStorage.setItem('partyToken', token)
store.dispatch({type: 'profile/setToken', payload: { token }})

извлечение токена из хранилища сеанса или состояния (хранилище редукта)

const token = window.sessionStorage.getItem('token')
const token = store.getState().profile && store.getState().profile.token

Конечно, вы можете определить общую функцию, в которой вы можете установить/обновить токен после каждого вызова API. Аналогично для поиска, потому что вам нужен токен перед вызовом API.