Перейти к содержимому
ГоМастер

Поиск по урокам

Найдите урок, курс, термин или раздел

Урок 1 из 25 ~16 мин +20 XP

Знакомство: аутентификация и авторизация в Go

Аутентификация golang сводится к одному вопросу: серверу нужно понять, кто стучится в дверь, — и JWT-токены стали самым популярным ответом на него. В этом вводном уроке курса jwt go разберём, чем вход в систему отличается от прав доступа, что берёт на себя токен и что остаётся за его пределами. Кода почти не будет: сначала карта местности, по которой пройдёт весь курс.

Содержание урока · 8 разделов

Начнём с простой сцены. Вы открыли интернет-магазин, залогинились, перешли в корзину — и сайт вас узнал. Со стороны кажется, что магии нет, но протокол HTTP не хранит состояния: каждый запрос приходит на сервер как незнакомец, без памяти о предыдущих. Кто-то обязан носить удостоверение личности с каждым обращением, и от того, как устроено это удостоверение, зависит половина архитектуры сервиса. Исторически сложилось два подхода. Первый — сессии: сервер выдаёт клиенту короткий случайный идентификатор в cookie и держит у себя таблицу «идентификатор → пользователь». Второй — токены: сервер выдаёт клиенту подписанный документ, в котором написано, кто этот клиент и до какого времени документ действителен, а хранить ничего не хранит. В свежих Go-сервисах, особенно в API и микросервисах, чаще встречается второй путь, и самым заметным его представителем стал JWT. В этом уроке пройдём обе схемы честно, чтобы ваш выбор был осознанным, а не скопированным из первого попавшегося туториала.

Аутентификация и авторизация: две разные проверки#

Эти два слова путают даже опытные разработчики, а разница между ними — половина вопросов собеседований по аутентификации golang. Аутентификация отвечает на вопрос «кто ты»: клиент предъявляет токен, пароль или отпечаток, сервер убеждается, что личность настоящая. Авторизация отвечает на вопрос «что тебе можно»: личность уже установлена, и сервер сверяет её права с запрошенным действием. Пользователь с ролью user, запрашивающий DELETE /admin/users, аутентифицирован идеально — и всё равно получит отказ, потому что прав у него нет. В HTTP для этого заведены два разных статуса: 401 Unauthorized — «не понял, кто ты, предъяви документ», и 403 Forbidden — «знаю, кто ты, но нельзя». Привычка отвечать 401 на всё подряд кажется мелочью, а стоит дорого: клиентские библиотеки по 401 запускают повторный вход, и пользователь с достаточными правами окажется выброшен на экран логина без причины. В середине курса мы соберём middleware, который проверяет обе вещи по очереди: сначала подпись и срок токена, потом роль из claims.

JWT-токены: что решает подпись и что не решает#

JWT-токены переворачивают схему сессий: данные о пользователе и срок годности подписываются сервером и уезжают к клиенту целиком. Сервер хранит единственную общую строку — секрет подписи; каждая реплика кластера проверяет токен самостоятельно, без похода в общее хранилище. Отсюда главное преимущество: горизонтальное масштабирование, при котором не нужно синхронизировать сессии между узлами, и рестарт процесса, который ничего не забывает. Подпись гарантирует целостность: изменить содержимое токена, не пересчитав подпись, нельзя, а пересчитать её без секрета — невозможно. Но у медали есть оборот, о котором узнают слишком поздно. Подпись не шифрует: любой, кто увидел токен — прокси, логи, расширение браузера, — прочтёт содержимое. Отзыв встроен не будет: токен валиден до exp, и «выкинуть» его досрочно по желанию сервера нельзя — для этого строят отдельные механизмы вроде чёрных списков, до которых курс доберётся в середине. И наконец, JWT не отменяет проверку прав: роль, записанная в токене, — утверждение на момент выпуска, а не живое состояние.

Настоящий токен и его разбор без секретов

GO
token := "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI0MiIsInJvbGUiOiJhZG1pbiJ9.-24izfUYTb3Z6pUvdW9g8q7V1MRiF5aJAtYiW-ZTKxw"

parts := strings.Split(token, ".")
fmt.Println("частей:", len(parts))

header, _ := base64.RawURLEncoding.DecodeString(parts[0])
payload, _ := base64.RawURLEncoding.DecodeString(parts[1])

fmt.Println(string(header))  // {"alg":"HS256","typ":"JWT"}
fmt.Println(string(payload)) // {"sub":"42","role":"admin"}

Настоящий HS256-токен выглядит именно так: три блока, разделённые точками. strings.Split режет строку, а base64.RawURLEncoding из стандартной библиотеки превращает блоки в читаемый JSON — ни секрет, ни внешняя библиотека для этого не нужны. Подпись в третьей части пока просто показана: как она считается и почему её нельзя обмануть, разберём в уроке про устройство токена.

Как бы ни была устроена схема, серверная проверка укладывается в пять шагов. Достать заголовок Authorization и убедиться, что он начинается со слова Bearer. Отрезать это слово и оставить строку токена. Проверить подпись — пересчитать её своим секретом и сравнить. Проверить срок: exp не должен остаться в прошлом, а nbf — в будущем. Достать из payload поля пользователя и положить их в контекст запроса, чтобы хендлеры не думали о токенах вовсе. Каждый шаг умеет отказывать по-своему: нет заголовка, кривая строка, чужая подпись, истёкший срок — и честный сервис отвечает на эти случаи по-разному, чему будет посвящён отдельный урок. Все пять шагов в живом сервисе прячет middleware: хендлер получает уже готового пользователя и занимается бизнес-логикой. Именно такой middleware мы соберём на chi и Gin в середине курса, а сейчас важно уложить в голове порядок: подпись проверяется раньше содержимого, а содержимое — раньше прав.

Схема JWT — не универсальный ответ, и честнее сравнить её с классикой. Cookie-сессии проще устроены: короткий идентификатор, хранилище на сервере, мгновенный отзыв — удалил запись, и пользователь разлогинен. Для монолитного сайта с шаблонами и формами я бы начал именно с них: меньше криптографии, меньше вопросов с хранением токена на клиенте. Отдельное хранилище сессий, например в Redis, поднимается за один вечер — на сайте есть курс про Redis в Go, где это разобрано по шагам. JWT окупается там, где клиентов много и они разные: мобильное приложение без cookie jar, несколько микросервисов, которым лень ходить в общий store за каждой проверкой, публичный API для сторонних разработчиков. Второй аргумент — нагрузка: проверка подписи дешевле, чем сетевой поход в хранилище на каждый запрос. Моё рабочее правило: один монолит и веб-интерфейс — cookie-сессии; распределённая система или API — JWT. Если сомневаетесь, посмотрите, как обе схемы живут во фреймворках: в курсе Gin есть уроки и про cookie-сессии, и про JWT — сравните на знакомом коде.

Две схемы аутентификации в одном кадре#

КритерийCookie-сессииJWT
Где живёт состояниев хранилище серверав самом токене
Масштабирование на репликиобщий store, синхронизациякаждая реплика проверяет подпись сама
Отзыв до истеченияудалить запись — мгновеннонужен отдельный denylist
Мобильные и CLI-клиентыимитация cookie jarодин заголовок Authorization
Размер на каждый запроскороткий идентификаторсотни байт

В жизненном цикле токена три роли, и полезно называть их по именам. Issuer — тот, кто выпускает: сервис логина, который проверил пароль, собрал claims и подписал токен. Проверяющий — любой сервис, к которому клиент придёт с этим токеном: он не знает пароля, не видел таблицы пользователей, но верит подписи, потому что умеет проверять её секретом или публичным ключом issuer-а. Носитель — сам клиент: браузер, мобильное приложение, другой бэкенд; его задача — хранить токен и прикладывать к запросам, не показывая никому лишнему. Разделение ролей объясняет, почему JWT так любят в микросервисах: auth-сервис выпускает, а остальные проверяют без единого похода к нему. Там же кроется ограничение, о котором стоит помнить заранее: чем дольше живёт токен, тем дольше он остаётся снимком момента выпуска. Поэтому короткий access-токен и refresh-схема появятся в курсе не как мода, а как следствие этого устройства.

Проверка: 401 или 403

Клиент предъявил валидный токен с ролью user и запросил DELETE /admin/users. Какой статус вернёт корректный сервис?

Проверка: что хранит сервер

Что хранит сервер, чтобы проверять подписи HS256-токенов сразу у всех пользователей?

Заглядывание вперёд: выпуск токена в пять строк

GO
secret := os.Getenv("JWT_SECRET")

t := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
        "sub":  "42",
        "role": "admin",
        "exp":  jwt.NewNumericDate(time.Now().Add(15 * time.Minute)),
})

signed, err := t.SignedString([]byte(secret))
if err != nil {
        log.Fatal(err)
}
fmt.Println(signed)

Это заглядывание в урок про установку библиотеки: jwt.NewWithClaims собирает заготовку токена из алгоритма и набора claims, SignedString подписывает её секретом из переменной окружения и отдаёт готовую строку. exp на пятнадцать минут — привычный срок жизни access-токена, запомните его. Полный разбор каждой строки — через два урока.

Что вернёт гвард на пустой заголовок?

Прочитайте код и выберите, что он выведет:

func guard(next http.HandlerFunc) http.HandlerFunc {
        return func(w http.ResponseWriter, r *http.Request) {
                token := r.Header.Get("Authorization")
                if token == "" {
                        w.WriteHeader(http.StatusUnauthorized)
                        return
                }
                if !allowed(r) {
                        w.WriteHeader(http.StatusForbidden)
                        return
                }
                next(w, r)
        }
}

// запрос: GET /profile без заголовка Authorization

Два отказа — два статуса

Допишите статусы для двух отказов: клиент без токена и клиент с недостаточными правами.

func profile(w http.ResponseWriter, r *http.Request) { if r.Header.Get("Authorization") == "" { w.WriteHeader(http.) return } if role := "user"; role != "admin" { w.WriteHeader(http.) return } w.Write([]byte("секретные данные")) }

Соберите main() с двумя защищёнными маршрутами

Расставьте строки минимального API так, чтобы оба маршрута подключились через один middleware-гвард guard().

Строки

Ваша программа

Нажимайте на строки слева, чтобы собрать программу

Карта курса jwt go: от первого токена до auth-сервиса#

Курс jwt go построен по нарастающей, и вводный урок — единственный без боевого кода. Дальше: устройство токена по слоям — header, payload, signature — в следующем уроке; установка golang-jwt v5 и первый токен в пять строк; claims со стандартными и своими полями; HMAC и HS256 с секретом и правилами его хранения. После этого серверная часть: проверка токена через ParseWithClaims, ошибки и честные ответы клиенту, refresh-токены с ротацией. Затем инфраструктура: middleware аутентификации, роли, RSA с асимметричными подписями, отзыв токенов и cookie-сессии. Финал — итоговый auth-сервис с регистрацией, логином и ролями. Одна честная оговорка заранее: это библиотечный курс, и его код потребует локального Go — браузерная песочница сайта не умеет ни внешние пакеты, ни сеть. Установка с go.dev занимает минут десять, а каждый урок снабжён домашним заданием под локальный запуск. Полный список тем всегда под рукой на странице курса JWT-аутентификации.

Карточки: базовые понятия

Термин

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

нажмите, чтобы перевернуть
Определение

Проверка личности: клиент предъявляет токен или пароль, сервер убеждается, что это он. Отказ — 401 Unauthorized.

1 / 5

Песочница урока

Редактируйте код и запускайте его прямо здесь — интерпретатор работает в браузере, ничего устанавливать не нужно. За каждый запуск — +2 XP.

Вывод

Запустите код, чтобы увидеть вывод.

Коротко: что запомнить

  • HTTP не помнит клиентов: удостоверение личности едет с каждым запросом — cookie-сессией или токеном.
  • Аутентификация отвечает на «кто ты» (401), авторизация — на «что тебе можно» (403); это две разные проверки.
  • JWT переносит состояние в сам токен: сервер хранит один секрет, реплики не синхронизируют сессии.
  • Подпись гарантирует целостность, но не конфиденциальность: payload читает каждый, кто увидел токен.
  • Токен без exp — вечный ключ; короткий срок жизни обязателен, долгие полномочия — у refresh-токена.
  • Cookie-сессии проще для монолита с веб-интерфейсом, JWT окупается в API и распределённых системах.
  • Серверная проверка — пять шагов от заголовка Authorization до пользователя в контексте запроса.
  • В схеме три роли: issuer выпускает, проверяющий сверяет подпись, носитель бережёт токен и прикладывает к запросам.

Домашнее задание

Без кода: возьмите любой сервис, которым пользуетесь, и опишите его аутентификацию по схеме урока — где живёт состояние, как выглядит удостоверение, что происходит при отзыве. Затем откройте DevTools, сеть одного запроса к API, и найдите заголовок Authorization: если там Bearer и длинная строка с двумя точками — перед вами JWT, разложите его на три части глазами и попробуйте назвать каждую роль из урока: кто выпустил, кто проверит, кто носит.

Личные заметкиПусто
Хранятся только в вашем браузере (JWT · Знакомство: аутентификация и авторизация в Go)

Вопросы и ответы по уроку

JWT — это шифрование?

Нет. JWT — подпись: она гарантирует, что содержимое не изменено после выпуска, но не прячет его. Любой, кто увидел токен, декодирует payload обычным base64url. Для сокрытия данных существует отдельный стандарт JWE, но в веб-аутентификации он встречается редко.

Почему нельзя класть пароль в JWT?

Payload токена кодируется, а не шифруется, и живёт у клиента до истечения exp. Пароль, персональные данные и любые секреты в claims — типовая утечка; в токен кладут только идентификатор, роль и служебные поля.

Можно ли обойтись без JWT вообще?

Да. Cookie-сессии с хранилищем на сервере решают ту же задачу и проще в отзыве. JWT выигрывает, когда клиентов много, они разнородны, или проверки должны идти без общего хранилища — например, в микросервисах и публичных API.

Термины урока

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

Следующая ступень учебного пути

Telegram25 уроков~12 чновичок25 уроков уже доступны

Telegram-боты на Go: от эха до полноценного сервиса

Учимся писать Telegram-ботов на Go: токен BotFather, команды, клавиатуры, состояния диалога, медиа, рассылки, webhook и деплой. 25 уроков с кодом.

  • Создавать бота через BotFather и хранить токен безопасно
  • Получать обновления через long polling и webhook
  • Отвечать сообщениями, командами и клавиатурами
  • Строить многошаговые диалоги с сохранением состояния