jwt практическое применение и мышление в особых случаях

Node.js

Веб-токен JSON Даrfc7519Стандарт, который использует JSON для передачи данных, чтобы определить, вошел ли пользователь в систему.

Перед jwt используйтеsessionдля аутентификации пользователя.

Следующий код написан на javascript.

session

Традиционный способ определить, следует ли входить в систему, — это использоватьsession + token.

tokenОтносится к использованию токенов на стороне клиента в качестве учетных данных статуса пользователя, которые обычно хранятся в браузере.localStorageилиcookieсередина.

sessionЭто относится к использованию базы данных redis или sql на стороне сервера для хранения парных отношений ключ-значение user_id и токена.Основной принцип работы заключается в следующем.

Использование на стороне сервераsessionsХранить пары ключ-значение

const sessions = {
  "ABCED1": 10086,
  "CDEFA0": 10010
}

Каждый раз, когда клиент запрашивает данные с разрешением, токен переносится, и серверная сторона получает user_id в соответствии с токеном и сеансами для завершения процесса аутентификации.

function getUserIdByToken (token) {
  return sessions[token]
}

если хранится вcookieчасто можно услышать вsession + cookieсхема входа. На самом деле хранится вcookie,localStorageчетноеIndexedDBилиWebSQLКаждый из них имеет свои преимущества и недостатки, и основная идея одна и та же.

оcookieа такжеtokenдостоинства и недостатки, вtoken authetication vs cookiesЕсть обсуждения.

Если вы не используете файлы cookie, вы можете воспользоватьсяlocalStorage + Authorizationспособ аутентификации, более без гражданства

// http 的头,每次请求权限接口时,需要携带 Authorization Header
const headers = {
  Authorization: `Bearer ${localStorage.get('token')}`
}

Порекомендуйте внешний репозиторийlocalForage,использоватьIndexedDB,WebSQLа такжеIndexedDBСделайте хранилище ключ-значение.

логин без гражданства

sessionНеобходимо хранить соответствующую информацию о пользователях и токенах в базе данных, поэтому она называетсясостояние.

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

первый метод:Внешний интерфейс напрямую передает user_id на сервер

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

Улучшать:Симметрично зашифровать user_id

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

Улучшать:User_id не нужно шифровать, нужно только подписать, чтобы гарантировать, что он не будет подделан

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

Json Web Token

jwt в соответствии сHeader,Payloadа такжеSignatureтри части по.сшиты вместе.

Header

Заголовок состоит из следующих алгоритмов и типов асимметричного шифрования.

const header = {
  // 加密算法
  alg: 'HS256',
  type: 'jwt'
}

Payload

Полезная нагрузка поRegistered Claimи состав данных, которые необходимо передать. Эти поля данных также называютсяClaim.

Registered Claimболее важно"exp" ClaimУказывает время истечения срока действия, которое будет установлено при входе пользователя в систему.

const payload = {
  // 表示 jwt 创建时间
  iat: 1532135735,

  // 表示 jwt 过期时间
  exp: 1532136735,

  // 用户 id,用以通信
  user_id: 10086
}

Signature

SignatureЗависит отHeader,Payloadа такжеsecretOrPrivateKeyвычислено.secretOrPrivateKeyПоскольку конфиденциальные данные хранятся на стороне сервера, рассмотрите возможность использованияvault secretилиk8s secret

заsecretOrPrivateKey, если алгоритм шифрования используетHMAC, то строка, еслиRSAилиECDSA, это PrivateKey.

// 由 HMACSHA256 算法进行签名,secret 不能外泄
const sign = HMACSHA256(base64.encode(header) + '.' + base64.encode(payload), secret)

// jwt 由三部分拼接而成
const jwt = base64.encode(header) + '.' + base64.encode(payload) + '.' + sign

Из сгенерированных правил jwt мы знаем, что клиент может анализировать полезную нагрузку, поэтому не переносите в полезную нагрузку конфиденциальные данные, такие как пароли пользователей.

процесс проверки

В правиле генерации видно, что первые две части jwt представляют собой кодировку base64 заголовка и полезной нагрузки.

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

С вопросом, как судить о сроке действия токена?

применение

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

Капча

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

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

Строка в паре с проверочным кодом может использоваться в качестве секрета для проверки без сохранения состояния.

const jwt = require('jsonwebtoken')

// 假设验证码为字符验证码,字符为 ACDE,10分钟失效
const token = jwt.sign({}, secrect + 'ACDE', { expiresIn: 60 * 10 })

const codeImage = getImageFromString('ACDE')

// 给前端的响应
const res = {
  // 验证码图片的 token,从中可以校验前端发送的验证码
  token,
  // 验证码图片
  codeImage,
}

SMS-код подтверждения совпадает с графическим кодом подтверждения.

Подтверждение по элетронной почте

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

// 把邮箱以及用户id绑定在一起
const code = jwt.sign({ email, userId }, secret, { expiresIn: 60 * 30 })

// 在此链接校验验证码
const link = `https://example.com/code=${code}`

Без гражданства против с сохранением состояния

Что касается stateless и stateful, то есть сравнения и в других технических направлениях, таких как React’sstateLess componentа такжеstateful component, побочные эффекты в функциональном программировании можно понимать как состояние, а http также является протоколом без сохранения состояния, который должен полагаться на заголовки и файлы cookie для переноса состояния.

В аутентификации пользователей состояние означает, следует ли полагаться на внешнее хранилище данных, такое как mysql, redis и т. д.

кейс

Рассмотрите следующие вопросы о входе в систему с использованием сеанса и реализации jwt, чтобы они были более понятными.jwtсценарии использования

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

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

  • session: Просто очистите токен, соответствующий user_id
  • jwt: Используйте Redis для поддержания черного списка. Когда пользователь выходит из системы, токен добавляется в черный список. Время истечения согласуется с временем истечения срока действия JWT.

Как разрешить пользователям входить в систему только на одном устройстве, например WeChat

  • session: использовать базу данных sql, добавить поле токена и индекс в таблицу пользовательской базы данных, сбрасывать поле токена каждый раз, когда вы входите в систему, и находить user_id в соответствии с токеном каждый раз, когда запрос требует интерфейса разрешений.
  • jwt: Если вы используете базу данных типа sql, добавьте поле токена в таблицу пользовательской базы данных (не нужно добавлять индекс), сбрасывайте поле токена каждый раз, когда вы входите в систему, и каждый раз, когда вы запрашиваете интерфейс разрешений, получайте user_id в соответствии с на jwt и проверьте таблицу пользователей в соответствии с user_id, чтобы получить оценку токена. Согласован ли токен. В качестве альтернативы вы можете использовать метод счетчика, как в следующем вопросе.

Для этого требования сессия немного проще, ведь jwt тоже должен полагаться на базу данных.

Как разрешить пользователям входить в систему только на последних пяти устройствах, таких как многие игроки

  • session: Создайте таблицу базы данных токенов, используя sql-подобную базу данных с тремя полями: id, token и user_id.Таблицы пользователей и токенов имеют отношение 1:m. Добавьте строку для каждого входа. Получите user_id в соответствии с токеном, а затем получите количество устройств, на которых пользователь вошел в систему в соответствии с user_id.Если их больше 5, удалите строку с наименьшим идентификатором.
  • jwt: использовать счетчик, использовать базу данных sql, добавить счетчик полей в пользовательскую таблицу, значение по умолчанию равно 0, поле счетчика увеличивается на 1 при каждом входе в систему, а полезная нагрузка jwt, создаваемая при каждом входе в систему, содержит данные current_count значение счетчика пользователя. Каждый раз, когда запрашивается интерфейс разрешений, счетчик и текущий_счет получаются из jwt, а счетчик получается из пользовательской таблицы в соответствии с user_id, и если разница с текущим_счетом меньше 5

Для этого требования jwt немного проще, а использование сеанса требует ведения дополнительной таблицы токенов.

Как разрешить пользователям входить в систему только на последних пяти устройствах и заставить пользователя отключать все другие устройства, кроме существующего устройства, например, многих игроков

  • session: на основании предыдущего вопроса удалите все записи токенов, кроме устройства.
  • jwt: На основании предыдущего вопроса считай +5, и переназначь устройство на новый кол.

Как отобразить список устройств, на которых зарегистрирован этот пользователь / Как выгнать определенного пользователя

  • session: добавить новое столбцовое устройство в таблицу токенов.
  • jwt: Сервер должен хранить информацию о списке устройств. Практика такая же, как и сеанс. Использование jwt не имеет смысла.

Суммировать

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

jwt не является панацеей.Использовать или не использовать jwt нужно решать в соответствии с потребностями бизнеса.


Я Shanyue, программист, обожаю бегать и лазить по горам, буду регулярно выкладывать full-stack статьи в личном паблике. Если вас интересуют интервью с полным стеком, фронтенд-инжиниринг, graphql, devops, эксплуатация и обслуживание персональных серверов и микросервисы, вы можете подписаться на меня

如果你对全栈面试,前端工程化,graphql,devops,个人服务器运维以及微服务感兴趣的话,可以关注我