Создайте безопасный API с помощью JWT

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

Проблема и решение

Эта проблема

Проблема здесь проста: у вас есть API, используемый клиентом (веб, мобильное приложение, интерфейс командной строки и т. Д.), И вы хотите защитить его и разрешить доступ к нему только зарегистрированным пользователям.

Решение

Решением будет использование веб-токенов JSON (для краткости JWT) для входа в систему и авторизации пользователей, как показано на простом изображении ниже.

  1. Клиент войдет в систему, отправив учетные данные на сервер API.
  2. Сервер API проверит учетные данные пользователя, подпишет JWT и вернет его в ответе HTTP.
  3. Клиент будет использовать полученный JWT для доступа к ресурсам API.
  4. Сервер API проверит JWT и разрешит пользователю доступ к ресурсу.

Что такое JWT?

Веб-токен JWT или JSON - это строка с цифровой подписью, используемая для безопасной передачи информации между сторонами. Это стандарт RFC7519.

JWT состоит из трех частей:

header.payload.signature

Ниже представлен образец JWT.

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

Заголовок

Заголовок представляет собой строку в кодировке Base64 и содержит тип токена (в данном случае JWT) и алгоритм подписи (в данном случае HMAC SHA256 или для краткости HS256).

{
  "alg": "HS256",
  "typ": "JWT"
}

Полезная нагрузка

Полезные данные - это строка в кодировке Base64, содержащая утверждения. Утверждения - это набор данных, связанных с пользователем и самим токеном. Примеры претензий: exp (срок действия), iat (выдан в), name (имя пользователя) и sub (тема).

{
  "sub": "1234567890",
  "name": "John Doe",
  "iat": 1516239022
}

Вы можете узнать больше о претензиях здесь.

Подпись

Подпись - это подписанная строка. Для алгоритмов подписи HMAC мы используем заголовок в кодировке Base64, полезные данные в кодировке Base64 и секрет подписи для их создания.

HMACSHA256(
  base64UrlEncode(header) + "." +
  base64UrlEncode(payload),
  secret)

JWT в Голанге

Теперь, когда у нас есть лучшее представление о JWT, давайте создадим наш небольшой API аутентификации на Golang.

Запустить модуль Go

Создайте новый каталог в вашем GOPATH, назовите его authapp, а затем запустите модуль Go.

go mod init

База данных

Чтобы упростить изучение, я собираюсь использовать базу данных SQLite.
Для работы с базами данных SQL в Go я настоятельно рекомендую использовать ORM, например GORM, поэтому давайте установим его вместе с драйвером SQLite. .

go get -u gorm.io/gorm
go get -u gorm.io/driver/sqlite

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

А теперь давайте проверим это.

Тест пройден, и наша база данных готова. Перейдем к пользовательской модели.

Модель пользователя

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

go get "golang.org/x/crypto/bcrypt"

Вот полная модель User с функциями для создания записи в базе данных, хэша и проверки пароля.

Теперь давайте проверим всю приведенную выше логику.

Все идет нормально. Теперь перейдем к подписанию JWT.

Подписание и проверка JWT

Чтобы подписать и проверить токены JWT в Golang, мы собираемся использовать пакет jwt-go.

go get github.com/dgrijalva/jwt-go

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

Я назвал его просто auth, и он имеет следующие структуры и функции:

  • структура JwtWrapper для обертывания ключа подписи, эмитента и срока действия в часах
  • структура JwtClaim для добавления настраиваемого электронного запроса к нашему токену
  • GenerateToken функция для генерации с использованием HS256 токена, срок действия которого истекает через 24 часа
  • ValidateToken функция, которая проверяет токен и возвращает утверждения

Ниже приведен полный код.

Время тестирования:

Большой! Теперь, когда у нас есть наша пользовательская модель и логика подписи / проверки JWT, пришло время создать фактические API.

API

В этом разделе мы собираемся создать три конечные точки RESTful API.

  • [POST] /api/public/signup = ›создает пользователя
  • [POST] /api/public/login = ›выполняет вход пользователя в систему и возвращает JWT
  • [GET] /api/private/profile = ›авторизует пользователя и возвращает запрошенные данные

Но прежде чем мы начнем, нам нужно установить отличный веб-фреймворк gin-gonic .

go get -u github.com/gin-gonic/gin

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

Давай проверим.

Наш HTTP-роутер готов. Теперь давайте создадим контроллер регистрации.

Регистрация

Регистрация - это общедоступный API; он не должен требовать аутентификации.

Чтобы создать контроллер джина, создайте пакет с именем controllers и файл с именем public.go.

Запрос POST. Мы должны получить полезную нагрузку и проверить ее, вставить пользователя в базу данных и вернуть вставленного пользователя.

Логин

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

Давайте проверим как регистрацию, так и вход в систему.

Здесь мы проводим модульное тестирование, но давайте создадим наши маршруты и протестируем реальную вещь в Postman.

Добавьте следующий код в файл main.go.

Теперь запустим сервер.

go run main.go

Давайте проверим это в Postman.

Очень хороший! Давайте проверим наш токен в JWT.

Потрясающие! Теперь пользователь может создать учетную запись, войти в систему и получить токен.

Давайте создадим защищенный маршрут, доступ к которому будут иметь только прошедшие аутентификацию пользователи.

Защищенный ресурс

Ресурс здесь будет профилем пользователя. Здесь все просто: он вернет данные пользователя клиенту.

Что-то здесь странное. Мы не проводили проверки токенов! Обратите внимание, как мы получили письмо из контекста джин.

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

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

Очень хороший. Теперь перейдем к промежуточному программному обеспечению.

Промежуточное ПО авторизации

Промежуточное ПО находится между клиентом и ресурсом, поэтому еще до того, как мы коснемся базы данных, будет вызвано промежуточное ПО для проверки токена и авторизации пользователя. Логика авторизации предельно проста:

  • Проверьте, присутствует ли JWT в заголовке авторизации.
  • Проверьте формат токена.
  • Подтвердите токен.
  • Перейдите к контроллеру.

Создайте пакет с именем middlewares и добавьте следующий код.

Время тестирования:

Отлично! Наше промежуточное ПО для авторизации прекрасно работает. Теперь давайте добавим правильный маршрут в наш основной маршрутизатор и сами посмотрим на результаты в Postman.

Похоже, это работает!

Резюме

Вот и все, ребята! Надеюсь, вам понравилась эта статья.

Я загрузил код на GitHub

Дополнительная литература: Обновить токены: когда их использовать и как они взаимодействуют с JWT