Представим, что мы разрабатываем страницу с профилем пользователя в социальной сети. У пользователей есть возможность редактировать свои профили. Нам нужно определять, что HTTP-запрос на изменение профиля пришел от владельца профиля. Если не сделать такую проверку, то злоумышленники смогут изменять данные профилей других пользователей.
В этом уроке мы разберем тему JWT-авторизации и ее реализацию в Go. Это важная тема, потому что авторизация — это ключевая часть безопасности любого веб-приложения.
Аутентификация и авторизация
Представим, что мы хотим войти в свой аккаунт социальной сети. Веб-сайту нужно понять, что мы владеем этим аккаунтом. Для этого нам нужно предоставить корректные учетные данные. Обычно это логин и пароль. Веб-сайт проверяет эти данные и, если они верны, мы входим в свой аккаунт. Этот процесс называется аутентификацией:
Допустим, после успешной аутентификации мы заходим на страницу со своим профилем. Так как мы уже были аутентифицированы, мы можем отправить запрос на редактирование данных профиля. Веб-сайт проверяет, что запрос пришел от владельца профиля, и позволяет изменять данные. Процесс проверки прав на осуществление действий называется авторизацией:
Таким образом, аутентификация — это процесс проверки учетных данных, а авторизация — это процесс проверки прав на осуществление действий. В примере социальной сети аутентификация происходит при логине, а авторизация при каждом запросе после аутентификации.
Процесс авторизации происходит часто, и если допустить ошибку в системе авторизации, то злоумышленники смогут осуществлять действия от имени других пользователей. Например, сделать покупку от их имени или узнать конфиденциальную информацию. Поэтому важно знать безопасные и проверенные способы авторизации.
Два самых популярных способа авторизации:
- С помощью сессий
- С помощью токенов
У каждого из этих способов есть свои уникальности, преимущества и недостатки. Ознакомиться более детально с ними можно в дополнительных материалах к уроку. Мы же рассмотрим на практике одну из реализаций авторизации с помощью токенов — JWT.
JWT-авторизация
JWT (JSON Web Token) — это специальный формат токена, который позволяет безопасно передавать данные между клиентом и сервером. Например, клиентом может быть веб-браузер или мобильное приложение, сервером — сервер с Go веб-приложением.
JWT-токен состоит из трех частей, которые разделены точкой:
- Header или заголовок — информация о токене, тип токена и алгоритм шифрования
- Payload или полезные данные — данные, которые мы хотим передать в токене. Например, имя пользователя, его роль, истекает ли токен. Эти данные представлены в виде JSON-объекта
- Signature или подпись — подпись токена, которая позволяет проверить, что токен не был изменен
Обычный токен имеет формат:
xxxxx.yyyyy.zzzzz
Рассмотрим пример реального токена с разбором каждой части. Предположим, наше веб-приложение сгенерировало следующий JWT-токен:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
Header
Заголовок обычно состоит из JSON-объекта с двумя свойствами:
- Тип токена, который в нашем случае — JWT
- Алгоритм шифрования, который в нашем случае — HMAC SHA256
Далее этот JSON-объект хэшируется с помощью Base64Url-кодирования, чтобы представить его в виде компактной строки.
Таким образом, в нашем примере заголовок JWT-токена имеет следующее значение:
{
"alg": "HS256",
"typ": "JWT"
}
Payload
Вторая часть токена — это полезная нагрузка в виде JSON-объекта. Она содержит различные данные об авторизованном пользователе. Значение этой части JWT-токена различно в каждом веб-приложении. Мы можем записать здесь любые публичные данные, которые могут быть полезны при авторизации.
Как и заголовок JWT-токена, полезная нагрузка хэшируется с помощью Base64Url-кодирования для представления в виде компактной строки.
В нашем примере полезная нагрузка JWT-токена имеет следующее значение:
{
"sub": "1234567890",
"name": "John Doe",
"iat": 1516239022
}
Названия некоторых полей могут показаться непонятными с первого взгляда. Например, поле sub означает идентификатор пользователя, а поле iat — время создания токена. При составлении полей полезной нагрузки рекомендуется учитывать имена из документации IANA (Internet Assigned Numbers Authority). Это поможет избежать конфликтов имен с общепринятыми нормами. Поэтому мы использовали название sub, вместо привычного user_id.
Основная причина, почему названия полей в полезной нагрузке JWT-токена пишутся сокращенно, — это уменьшение размера токена после шифрования.
Signature
Чтобы создать подпись, мы должны взять закодированный заголовок, закодированную полезную нагрузку, секретную строку и зашифровать эти данные. При этом нужно использовать алгоритм шифрования из заголовка JWT-токена.
В нашем примере для создания подписи используется алгоритм шифрования HMAC SHA256 и секретная строка "your-256-bit-secret":
HMACSHA256(
base64UrlEncode(header) + "." +
base64UrlEncode(payload),
your-256-bit-secret
)
Подпись используются, чтобы проверить, что сообщение не было изменено при передаче. Она также позволяет подтвердить, что отправитель JWT-токена является тем, кем он представляется.
Собираем все части JWT-токена
В результате генерации JWT-токена получаются три Base64-URL-закодированные строки, которые разделены точкой. Значение JWT-токена является компактным и легко передается в HTTP-запросах.
Если вы хотите попрактиковаться с JWT, можно использовать онлайн инструмент jwt.io Debugger для декодирования, проверки и генерации JWT-токенов.
Мы разобрали, из чего состоит JWT-токен. Теперь рассмотрим алгоритм работы с JWT-токеном в веб-приложениях.
Алгоритм работы с JWT-токеном
Процесс аутентификации и авторизации с JWT-токеном между веб-браузером и веб-приложением выглядит следующим образом:
- Веб-браузер отправляет запрос веб-приложению с логином и паролем
- Веб-приложение проверяет логин и пароль, и если они верны, то генерирует JWT-токен и отправляет его веб-браузеру. При генерации JWT-токена веб-приложение ставит подпись секретным ключом, который хранится только в веб-приложении
- Веб-браузер сохраняет JWT-токен и отправляет его вместе с каждым запросом в веб-приложение
- Веб-приложение проверяет JWT-токен и если он верный, то выполняет действие от имени авторизованного пользователя
Безопасность коммуникации между веб-браузером и веб-приложением заключается в том, что токены генерируются и подписываются только со стороны веб-приложения. Злоумышленник не сможет подделать токен, так как не знает секретный ключ, который используется для подписи токена.
Подпись токена происходит с помощью шифрования. С помощью подписи веб-приложение проверяет, что токен действительно был сгенерирован им. Шифрование может осуществляться различными алгоритмами. Например, алгоритмом HS256
— HMAC с SHA-256.
Мы рассмотрели основы JWT-авторизации и поняли, что веб-приложение должно генерировать JWT-токены и подписывать их секретным ключом, который хранится только в веб-приложении. Рассмотрим, как это можно реализовать в Go-приложении.
JWT-авторизация в Go веб-приложении
Реализуем аутентификацию и авторизацию в социальной сети, которая написана на Go с использованием микрофреймворка Fiber. Для этого реализуем следующие функции:
- Регистрация пользователя
- Вход в аккаунт — аутентификация
- Получение информации о своем аккаунте — только для авторизованных пользователей
Чтобы упростить работу, мы не будем описывать многие важные части в системах аутентификации пользователей. Например, мы не будем проверять HTTP-запросы, использовать базы данных или хэшировать хранимые пароли для безопасности. Вы можете улучшить эти части веб-приложения самостоятельно.
Регистрация аккаунта
Начнем разработку социальной сети с функции регистрации аккаунта. Когда пользователь заходит на веб-сайт, он видит форму регистрации с тремя полями: имя, электронная почта и пароль. После заполнения формы пользователь нажимает кнопку «Зарегистрироваться», и веб-браузер отправляет HTTP-запрос POST /register
в наше веб-приложение. Мы реализуем обработчик этого HTTP-запроса следующим образом:
package main
import (
"errors"
"fmt"
"github.com/gofiber/fiber/v2"
"github.com/sirupsen/logrus"
)
func main() {
app := fiber.New()
authHandler := &AuthHandler{&AuthStorage{map[string]User{}}}
app.Post("/register", authHandler.Register)
logrus.Fatal(app.Listen(":80"))
}
type (
// Обработчик HTTP-запросов на регистрацию и аутентификацию пользователей
AuthHandler struct {
storage *AuthStorage
}
// Хранилище зарегистрированных пользователей
// Данные хранятся в оперативной памяти
AuthStorage struct {
users map[string]User
}
// Структура данных с информацией о пользователе
User struct {
Email string
Name string
password string
}
)
// Структура HTTP-запроса на регистрацию пользователя
type RegisterRequest struct {
Email string `json:"email"`
Name string `json:"name"`
Password string `json:"password"`
}
// Обработчик HTTP-запросов на регистрацию пользователя
func (h *AuthHandler) Register(c *fiber.Ctx) error {
regReq := RegisterRequest{}
if err := c.BodyParser(®Req); err != nil {
return fmt.Errorf("body parser: %w", err)
}
// Проверяем, что пользователь с таким email еще не зарегистрирован
if _, exists := h.storage.users[regReq.Email]; exists {
return errors.New("the user already exists")
}
// Сохраняем в память нового зарегистрированного пользователя
h.storage.users[regReq.Email] = User{
Email: regReq.Email,
Name: regReq.Name,
password: regReq.Password,
}
return c.SendStatus(fiber.StatusCreated)
}
Когда приходит HTTP-запрос на регистрацию нового пользователя, веб-приложение проверяет, что в хранилище еще не существует пользователя с такой электронной почтой. Если пользователь с такой электронной почтой уже есть, то веб-приложение возвращает ошибку. В обратном случае все данные пользователя сохраняются в оперативной памяти веб-приложения.
После регистрации пользователь может войти в свой аккаунт, и далее мы реализуем эту возможность.
Вход в аккаунт
Когда пользователь заходит на страницу входа в аккаунт, он видит форму с двумя полями: электронная почта и пароль. Эти поля являются учетными данными пользователя. После заполнения формы пользователь нажимает кнопку «Войти», и веб-браузер отправляет HTTP-запрос POST /login
в наше веб-приложение. Обработчик этого запроса будет выглядеть следующим образом:
// Структура HTTP-запроса на вход в аккаунт
type LoginRequest struct {
Email string `json:"email"`
Password string `json:"password"`
}
// Структура HTTP-ответа на вход в аккаунт
// В ответе содержится JWT-токен авторизованного пользователя
type LoginResponse struct {
AccessToken string `json:"access_token"`
}
var (
errBadCredentials = errors.New("email or password is incorrect")
)
// Секретный ключ для подписи JWT-токена
// Необходимо хранить в безопасном месте
var jwtSecretKey = []byte("very-secret-key")
// Обработчик HTTP-запросов на вход в аккаунт
func (h *AuthHandler) Login(c *fiber.Ctx) error {
regReq := LoginRequest{}
if err := c.BodyParser(®Req); err != nil {
return fmt.Errorf("body parser: %w", err)
}
// Ищем пользователя в памяти приложения по электронной почте
user, exists := h.storage.users[regReq.Email]
// Если пользователь не найден, возвращаем ошибку
if !exists {
return errBadCredentials
}
// Если пользователь найден, но у него другой пароль, возвращаем ошибку
if user.password != regReq.Password {
return errBadCredentials
}
// Генерируем JWT-токен для пользователя,
// который он будет использовать в будущих HTTP-запросах
// Генерируем полезные данные, которые будут храниться в токене
payload := jwt.MapClaims{
"sub": user.Email,
"exp": time.Now().Add(time.Hour * 72).Unix(),
}
// Создаем новый JWT-токен и подписываем его по алгоритму HS256
token := jwt.NewWithClaims(jwt.SigningMethodHS256, payload)
t, err := token.SignedString(jwtSecretKey)
if err != nil {
logrus.WithError(err).Error("JWT token signing")
return c.SendStatus(fiber.StatusInternalServerError)
}
return c.JSON(LoginResponse{AccessToken: t})
}
Когда приходит HTTP-запрос на вход в аккаунт, веб-приложение проверяет, что в хранилище существует пользователь с такой электронной почтой и паролем. Если учетные данные некорректны, то веб-приложение возвращает ошибку. В обратном случае пользователь успешно прошел аутентификацию, и веб-приложение генерирует и возвращает JWT-токен. Его пользователь будет использовать в будущих HTTP-запросах, чтобы авторизоваться.
Чтобы сгенерировать JWT-токен, мы используем библиотеку jwt-go. Благодаря этому все тонкости формирования и шифрования токена скрыты от нас. Все, что от нас требуется, — это указать секретный ключ для подписи токена и полезные данные, которые будут храниться в токене.
В полезных данных JWT-токена мы записываем электронную почту пользователя как идентификатор. Также записываем время, когда этот токен будет недействителен. В нашем примере время жизни JWT-токена составляет 72 часа. Когда это время истечет, пользователь должен будет войти в аккаунт заново.
Теперь в нашей социальной сети пользователи могут регистрироваться и аутентифицироваться — входить в свои аккаунты. Далее реализуем функцию получения информации о своем аккаунте, которая будет доступна только авторизованным пользователям.
Получение информации о своем аккаунте для авторизованных пользователей
Когда пользователь прошел аутентификацию, он получил JWT-токен, который будет использовать в последующих HTTP-запросах для авторизации. Есть разные способы передавать токен в HTTP-запросе. Для этого можно использовать заголовок Authorization, параметр запроса или даже куки веб-браузера. Мы будем использовать заголовок Authorization, так как это наиболее распространенный способ передачи JWT-токена в HTTP-запросе.
Когда пользователь заходит на свою страницу, веб-браузер отправляет HTTP-запрос GET /profile
в наше веб-приложение. Обработчик этого запроса выглядит следующим образом:
package main
import (
...
jwtware "github.com/gofiber/contrib/jwt"
jwt "github.com/golang-jwt/jwt/v5"
...
)
const (
contextKeyUser = "user"
)
func main() {
app := fiber.New()
...
// Группа обработчиков, которые требуют авторизации
authorizedGroup := app.Group("")
authorizedGroup.Use(jwtware.New(jwtware.Config{
SigningKey: jwtware.SigningKey{
Key: jwtSecretKey,
},
ContextKey: contextKeyUser,
}))
authorizedGroup.Get("/profile", userHandler.Profile)
logrus.Fatal(app.Listen(":80"))
}
// Структура HTTP-ответа с информацией о пользователе
type ProfileResponse struct {
Email string `json:"email"`
Name string `json:"name"`
}
func jwtPayloadFromRequest(c *fiber.Ctx) (jwt.MapClaims, bool) {
jwtToken, ok := c.Context().Value(contextKeyUser).(*jwt.Token)
if !ok {
logrus.WithFields(logrus.Fields{
"jwt_token_context_value": c.Context().Value(contextKeyUser),
}).Error("wrong type of JWT token in context")
return nil, false
}
payload, ok := jwtToken.Claims.(jwt.MapClaims)
if !ok {
logrus.WithFields(logrus.Fields{
"jwt_token_claims": jwtToken.Claims,
}).Error("wrong type of JWT token claims")
return nil, false
}
return payload, true
}
// Обработчик HTTP-запросов на получение информации о пользователе
func (h *UserHandler) Profile(c *fiber.Ctx) error {
jwtPayload, ok := jwtPayloadFromRequest(c)
if !ok {
return c.SendStatus(fiber.StatusUnauthorized)
}
userInfo, ok := h.storage.users[jwtPayload["sub"].(string)]
if !ok {
return errors.New("user not found")
}
return c.JSON(ProfileResponse{
Email: userInfo.Email,
Name: userInfo.Name,
})
}
Так как проверка авторизации может происходить во многих HTTP-обработчиках, мы вынесли ее на уровень посредников. В микрофреймворке Fiber для этого есть готовый посредник — jwtware.
При инициализации посредника мы указали два свойства:
- SigningKey — секретный ключ JWT-токена
- ContextKey — название поля, по которому хранится объект JWT-токена авторизованного пользователя. Этот объект можно использовать в любом обработчике группы
authorizedGroup
Когда приходит HTTP-запрос на получение информации о пользователе, веб-приложение проверяет, что в заголовке Authorization указан корректный JWT-токен. Если проверка прошла успешно, веб-приложение ищет пользователя в хранилище по электронной почте, которая записана в полезной нагрузке JWT-токена. Если пользователь был найден, то авторизация пройдена, и мы возвращаем в HTTP-ответе информацию об этом пользователе.
Мы реализовали все части аутентификации и авторизации нашей социальной сети. Теперь соберем все вместе и проверим, что веб-приложение работает.
Проверяем веб-приложение
Полный код веб-приложения выглядит следующим образом:
package main
import (
"errors"
"fmt"
"github.com/gofiber/fiber/v2"
jwtware "github.com/gofiber/contrib/jwt"
jwt "github.com/golang-jwt/jwt/v5"
"github.com/sirupsen/logrus"
"time"
)
const (
contextKeyUser = "user"
)
func main() {
app := fiber.New()
authStorage := &AuthStorage{map[string]User{}}
authHandler := &AuthHandler{storage: authStorage}
userHandler := &UserHandler{storage: authStorage}
// Группа обработчиков, которые доступны неавторизованным пользователям
publicGroup := app.Group("")
publicGroup.Post("/register", authHandler.Register)
publicGroup.Post("/login", authHandler.Login)
// Группа обработчиков, которые требуют авторизации
authorizedGroup := app.Group("")
authorizedGroup.Use(jwtware.New(jwtware.Config{
SigningKey: jwtware.SigningKey{
Key: jwtSecretKey,
},
ContextKey: contextKeyUser,
}))
authorizedGroup.Get("/profile", userHandler.Profile)
logrus.Fatal(app.Listen(":80"))
}
type (
// Обработчик HTTP-запросов на регистрацию и аутентификацию пользователей
AuthHandler struct {
storage *AuthStorage
}
// Хранилище зарегистрированных пользователей
// Данные хранятся в оперативной памяти
AuthStorage struct {
users map[string]User
}
// Структура данных с информацией о пользователе
User struct {
Email string
Name string
password string
}
)
// Структура HTTP-запроса на регистрацию пользователя
type RegisterRequest struct {
Email string `json:"email"`
Name string `json:"name"`
Password string `json:"password"`
}
// Обработчик HTTP-запросов на регистрацию пользователя
func (h *AuthHandler) Register(c *fiber.Ctx) error {
regReq := RegisterRequest{}
if err := c.BodyParser(®Req); err != nil {
return fmt.Errorf("body parser: %w", err)
}
// Проверяем, что пользователь с таким email еще не зарегистрирован
if _, exists := h.storage.users[regReq.Email]; exists {
return errors.New("the user already exists")
}
// Сохраняем в память нового зарегистрированного пользователя
h.storage.users[regReq.Email] = User{
Email: regReq.Email,
Name: regReq.Name,
password: regReq.Password,
}
return c.SendStatus(fiber.StatusCreated)
}
// Структура HTTP-запроса на вход в аккаунт
type LoginRequest struct {
Email string `json:"email"`
Password string `json:"password"`
}
// Структура HTTP-ответа на вход в аккаунт
// В ответе содержится JWT-токен авторизованного пользователя
type LoginResponse struct {
AccessToken string `json:"access_token"`
}
var (
errBadCredentials = errors.New("email or password is incorrect")
)
// Секретный ключ для подписи JWT-токена
// Необходимо хранить в безопасном месте
var jwtSecretKey = []byte("very-secret-key")
// Обработчик HTTP-запросов на вход в аккаунт
func (h *AuthHandler) Login(c *fiber.Ctx) error {
regReq := LoginRequest{}
if err := c.BodyParser(®Req); err != nil {
return fmt.Errorf("body parser: %w", err)
}
// Ищем пользователя в памяти приложения по электронной почте
user, exists := h.storage.users[regReq.Email]
// Если пользователь не найден, возвращаем ошибку
if !exists {
return errBadCredentials
}
// Если пользователь найден, но у него другой пароль, возвращаем ошибку
if user.password != regReq.Password {
return errBadCredentials
}
// Генерируем JWT-токен для пользователя,
// который он будет использовать в будущих HTTP-запросах
// Генерируем полезные данные, которые будут храниться в токене
payload := jwt.MapClaims{
"sub": user.Email,
"exp": time.Now().Add(time.Hour * 72).Unix(),
}
// Создаем новый JWT-токен и подписываем его по алгоритму HS256
token := jwt.NewWithClaims(jwt.SigningMethodHS256, payload)
t, err := token.SignedString(jwtSecretKey)
if err != nil {
logrus.WithError(err).Error("JWT token signing")
return c.SendStatus(fiber.StatusInternalServerError)
}
return c.JSON(LoginResponse{AccessToken: t})
}
// Обработчик HTTP-запросов, которые связаны с пользователем
type UserHandler struct {
storage *AuthStorage
}
// Структура HTTP-ответа с информацией о пользователе
type ProfileResponse struct {
Email string `json:"email"`
Name string `json:"name"`
}
func jwtPayloadFromRequest(c *fiber.Ctx) (jwt.MapClaims, bool) {
jwtToken, ok := c.Context().Value(contextKeyUser).(*jwt.Token)
if !ok {
logrus.WithFields(logrus.Fields{
"jwt_token_context_value": c.Context().Value(contextKeyUser),
}).Error("wrong type of JWT token in context")
return nil, false
}
payload, ok := jwtToken.Claims.(jwt.MapClaims)
if !ok {
logrus.WithFields(logrus.Fields{
"jwt_token_claims": jwtToken.Claims,
}).Error("wrong type of JWT token claims")
return nil, false
}
return payload, true
}
// Обработчик HTTP-запросов на получение информации о пользователе
func (h *UserHandler) Profile(c *fiber.Ctx) error {
jwtPayload, ok := jwtPayloadFromRequest(c)
if !ok {
return c.SendStatus(fiber.StatusUnauthorized)
}
userInfo, ok := h.storage.users[jwtPayload["sub"].(string)]
if !ok {
return errors.New("user not found")
}
return c.JSON(ProfileResponse{
Email: userInfo.Email,
Name: userInfo.Name,
})
}
Запускаем веб-приложение и отправляем запрос на регистрацию нового пользователя:
curl --location --request POST 'http://localhost/register' \
--header 'Content-Type: application/json' \
--data-raw '{
"email": "john@doe.com",
"name": "John",
"password": "pickles"
}'
В ответ получаем:
HTTP/1.1 201 Created
Это означает, что такого пользователя нет в хранилище, и он был успешно создан.
Теперь попробуем пройти аутентификацию этого пользователя:
curl --location --request POST 'http://localhost/login' \
--header 'Content-Type: application/json' \
--data-raw '{
"email": "john@doe.com",
"password": "pickles"
}'
В ответ приходит:
HTTP/1.1 200 OK
{"access_token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2Njg5NTEwMTcsInN1YiI6ImpvaG5AZG9lLmNvbSJ9.Q3k6yMFYtuzPyjoZYpIHibJQPey29QWmlHfwS2A3keM"}
Мы указали корректные учетные данные пользователя, поэтому аутентификация прошла успешно. В ответ веб-приложение вернуло JWT-токен, который мы будем использовать для авторизации при получении информации о пользователе.
Отправляем запрос на получение информации о пользователе:
curl -v 'http://localhost/profile' \
--header 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2Njg5NTE0NDEsInN1YiI6ImpvaG5AZG9lLmNvbSJ9.e4yIoGzQC8ckcRISBjt4g18S2VEBiHrRhXG7N39-7qI'
В ответ приходит:
HTTP/1.1 200 OK
{"email":"john@doe.com","name":"John"}
Так как мы передали JWT-токен в заголовке HTTP-запроса Authorization, веб-приложение авторизовало нас как пользователя john@doe.com, и вернуло информацию о нашем аккаунте.
В последнем запросе есть один интересный момент. Мы поставили перед значением токена слово Bearer:
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2Njg5NTE0NDEsInN1YiI6ImpvaG5AZG9lLmNvbSJ9.e4yIoGzQC8ckcRISBjt4g18S2VEBiHrRhXG7N39-7qI
Bearer переводится как носитель. Он дает веб-приложению понять, что в заголовке Authorization передан токен для авторизации. Принято считать, что это слово означает фразу: «Дай доступ к носителю этого токена». Если не указать слово Bearer перед значением, то авторизация не пройдет даже с корректным значением токена.
Мы реализовали функцию регистрации, аутентификации и получение информации об аккаунте авторизованного пользователя. Для авторизации мы использовали JWT-токен, который генерируются при входе в аккаунт на 72 часа и передается в заголовке HTTP-запроса Authorization.
Выводы
- Аутентификация — это процесс проверки подлинности пользователя, который пытается получить доступ к веб-приложению
- Авторизация — это процесс проверки прав пользователя на какое-либо действие в веб-приложении
- Один из способов реализации системы авторизации — это JWT-токен. Токен генерируется и подписывается веб-приложением после успешной аутентификации и передается во всех последующих HTTP-запросах в заголовке Authorization
- JWT-токен содержит информацию о пользователе и подпись, которая необходима для авторизации. Так как токен подписан секретным ключом в веб-приложении, то его может проверять на подлинность только это веб-приложение.
Дополнительные материалы
Остались вопросы? Задайте их в разделе «Обсуждение»
Вам ответят команда поддержки Хекслета или другие студенты
- Статья «Как учиться и справляться с негативными мыслями»
- Статья «Ловушки обучения»
- Статья «Сложные простые задачи по программированию»
- Вебинар «Как самостоятельно учиться»
Для полного доступа к курсу нужен базовый план
Базовый план откроет полный доступ ко всем курсам, упражнениям и урокам Хекслета, проектам и пожизненный доступ к теории пройденных уроков. Подписку можно отменить в любой момент.