Как пройти аутентификацию с помощью JWT.
Как пройти аутентификацию с помощью веб-токена JSON (JWT)
В этой статье вы узнаете:
• Что такое JSON Web Token (JWT).
• Как создать веб-маркер JSON (JWT).
• Как пройти аутентификацию и авторизацию с помощью веб-токена JSON (JWT)
Введение
JSON Web Token — это стандарт для создания и обмена зашифрованными данными.
Структура JWT:
JWT содержит Заголовок, Полезную нагрузку и Подпись.
Заголовок
Первая часть JWT — это заголовок. Он содержит алгоритм, который будет использоваться для шифрования и типа токена.
Вот пример заголовка:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
А вот пример заголовка в формате JSON:
{
“alg”: “HS256”,
“typ”: “JWT”
}
Полезная нагрузка
Вторая часть JWT — это полезная нагрузка. Она содержит утверждения, которые представляют собой утверждения о пользователе и дополнительные данные.
Вот пример полезной нагрузки:
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ
А вот пример полезной нагрузки в формате JSON:
{
“sub”: “1234567890”,
“name”: “John Doe”,
“iat”: 1516239022
}
Подпись
Третьей или последней частью JWT является подпись. Она используется для проверки владельца сообщения и, если она не была изменена по пути, для создания подписи заголовок в кодировке Base64, Payload и Secret берутся, затем подписываются по алгоритму, указанному в заголовке.
Пример подписи:
SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
А вот как создается подпись:
HMACSHA256( base64UrlEncode(header) + “.” + base64UrlEncode(payload), your-256-bit-secret ) secret base64 encoded
Предпосылки
Поскольку мы будем создавать серверную службу с помощью Node.js и Express, знание всего этого является преимуществом, хотя я проведу вас через это, если вы этого не знаете.
• JavaScript
• Express.js
• Некоторые навыки контроля версий
Я ожидаю, что у вас есть Node.js, любой редактор кода, платформа для тестирования API, такая как Postman, MongoDB или любая другая установленная база данных.
Настройка сервера
Создайте файл и назовите его как угодно. Я назову свой `fullauth`, а затем инициализирую папку командой ` git init `.
Теперь введите `code .` в своем терминале, чтобы открыть файл, это будет работать, только если вы используете VsCode.
Следующее, что мы хотим сделать, это инициализировать npm, выполнив `npm init -y` в нашем терминале, и у нас должен быть наш `package.json` в нашей папке.
Теперь нам нужно создать все папки и файлы, которые нам нужны, мы можем легко сделать это с помощью этой команды:
mkdir config controller middleware model routes touch config/connect.js controller/authcontroller.js middleware/auth.js model/user.js routes/authroute.js .gitignore app.js index.js .env
И наша папка должна выглядеть так:

Установка зависимостей
И теперь мы установим все необходимые нам зависимости с помощью этой команды:
npm i mongoose dotenv express jsonwebtoken bcryptjs nodemon
Теперь мы добавим `“dev”: “nodemon index.js”` в скрипты в файле `package.json` и `”type”: “module”`, первое для автоматического перезапуска сервера, а второе для включите модули ES6, наш файл package.json должен выглядеть так:
{
“name”: “fullauth”,
“version”: “1.0.0”,
“description”: “”,
“main”: “index.js”,
“scripts”: {
“test”: “echo \”Error: no test specified\” && exit 1",
“dev”: “nodemon index.js”
},
“keywords”: [],
“author”: “”,
“license”: “ISC”,
“dependencies”: {
“bcryptjs”: “².4.3”,
“dotenv”: “¹⁶.0.3”,
“express”: “⁴.18.2”,
“jsonwebtoken”: “⁸.5.1”,
“mongoose”: “⁶.7.0”,
“nodemon”: “².0.20”
},
“type”: “module”
}
Начиная
Создание сервера Node.js
Подключение к локальной MongoDB
Мы добавим это в файл `connect.js`:
import mongoose from “mongoose”;
import “dotenv/config”;
const connect = {
connectToLocalMongoDB : () =>{
mongoose
.connect(process.env.MONGO_URI_LOCAL, {
useNewUrlParser: true,
useUnifiedTopology: true,
})
.then(() => console.log(“DB Connected!”))
.catch((err) => {
console.log(“not able to connect to database”+ err);
});
}
}
export default connect;
Добавление `MONGO_URI_LOCAL` в файл `.env`:
MONGO_URI_LOCAL=mongodb://127.0.0.1:27017/fullauth
Обновление файла `app.js`:
import express from ‘express’
import connect from “./config/connect.js”;
const app = express()
app.use(
express.urlencoded({
extended: false,
})
);
app.use(express.json());
connect.connectToLocalMongoDB()
export default app
Запуск сервера с файлом `index.js`
Мы добавим это в наш файл index.js:
import “dotenv/config”;
import app from “./app.js”;
const PORT = process.env.PORT || 8080
app.get(‘/’, (req, res) =>{
res.send(“Welcome to Full authentication app”)
})
app.listen(PORT, () =>{
console.log(`app is listening on port ${PORT}`)
})
Добавление PORT в файл .env:
PORT=8080
Теперь вы можете запустить `npm run dev`, чтобы запустить сервер.
Создание модели пользователя в файле `user.js`:
Мы создадим модель пользователя в файле `user.js`, это то, что мы будем вызывать для выполнения операций с базой данных:
import mongoose from “mongoose”;
const User = new mongoose.Schema({
first_name: {type: String, default: null},
last_name: {type: String, default: null},
email: {type: String, default: null},
password: {type: String, default: null},
})
export default mongoose.model(“user”, User);
Реализация контроллеров SignUp и SignIn
Мы хотим реализовать контроллер signUp, конечная точка `/signup` будет подключена к этому контроллеру, чтобы новый пользователь мог зарегистрироваться.
Что мы будем делать:
- проверить, существует ли пользователь
- проверить, не пусто ли какое-либо поле
- создать нового пользователя и JWT
Импорт зависимостей в файл `authcontroller.js`:
import “dotenv/config”; import user from “../model/user.js”; import bcrypt from “bcryptjs”; import jwt from “jsonwebtoken”;
Создание контроллера SignUp в файле `authcontroller.js`:
export const signUp = async (req, res) =>{
const { firstname, lastname, email, password } = req.body
const oldUser = await user.findOne({email: email})
if(oldUser){
return res.status(424).json({
status: false,
message: “user already exists”
})
}
if(!(firstname && lastname && email && password)){
return res.status(424).json({
status: false,
message: “all fields are required”
})
}
if( oldUser == null ){
const encrptedPassword = await bcrypt.hash(password, 12);
const newUser = await user.create({
first_name: firstname,
last_name: lastname,
email: email,
password: encrptedPassword,
})
const token = jwt.sign({id: newUser._id, }, process.env.JWT_SECRET_KEY,{expiresIn: “2d”});
return res.status(200).json({
status: true,
message: “Registration successful”,
data: {
token: token
}
})
}
}
Что мы сделали :
1. Мы импортировали все зависимости, которые будем использовать
2. Мы проверили, существует ли пользователь
3. Создаем нового пользователя, если он не существует.
4. Мы использовали Jwt.sign для создания нового токена, метод `.sign` принимает параметры, которые включают Payload, он содержит утверждения, которые являются заявлениями о пользователе и дополнительными данными, второй параметр секретный ключ, который мы будем использовать для создания уникального хэша, а затем последний параметр, который я добавил, — это параметр подписи, устанавливающий время истечения срока действия на 2 дня.
Создание контроллера SignIn в файле `authcontroller.js`:
export const signIn = async (req, res) =>{
try {
const { email, password } = req.body;
const oldUser = await user.findOne({email: email})
if(!(email && password)){
return res.status(424).json({
status: false,
message: “all fields are required”
})
}
if( oldUser == null ){
return res.status(424).json({
status: false,
message: “User does not exist”
})
}
if( oldUser && (await bcrypt.compare(password, oldUser.password))){
const token = jwt.sign({id: oldUser._id, email:email }, process.env.JWT_SECRET_KEY,{expiresIn: “2d”});
return res.status(200).json({
status: true,
message: “Login Successful”,
data:{
token: token
}
})
}else{
return res.status(424).json({
status: false,
message: “Incorrect email or password”
})
}
} catch (error) {
return res.status(500).json({
status: false,
message: “An error occurred”
})
}
}
Что мы сделали :
1. Проверьте, существует ли пользователь, используя указанный адрес электронной почты.
2. Если пользователь существует и пароль совпадает с паролем в базе данных, создайте JWT с идентификатором и адресом электронной почты в качестве полезной нагрузки, секретом в нашем файле `.env` в качестве подписи и установите срок действия 2 дня.
3. Вернуть ответ, который включает JWT.
Создание промежуточного ПО, которое проверяет и декодирует JWT в файле `auth.js`:
import jwt from “jsonwebtoken”;
export const verifyToken = async (req, res, next) =>{
try {
const token = req.body.token || req.query.token || req.headers[‘access-token’]
if(!token){
return res.status(403).json({
status: false,
message: `A token is required for authentication`
})
}
const decoded = jwt.verify(token, process.env.JWT_SECRET_KEY)
req.user = decoded
} catch (error) {
return res.status(401).json({
status: false,
message: `Invalid Token`
})
}
return next();
}
Что мы сделали :
1. Проверьте наличие JWT в теле запроса, запросе или заголовке с помощью «токена доступа» и ключа.
2. Если предоставляется JWT, декодируйте его с помощью метода `jwt.verify`, этот метод принимает два параметра, то есть JWT и секретный ключ, который использовался для его подписи.
3. Назначьте декодированный объект ` req.user `, чтобы маршруты после аутентификации могли его оценить.
4. Верните «Неверный код», если срок действия кода истек или он был изменен.
Создание контроллера, который приветствует аутентифицированного пользователя в файле `authcontroller.js`:
export const greetUser = async (req, res) =>{
try {
const id = req.user.id;
const oldUser = await user.findById(id);
if( oldUser ){
return res.status(200).json({
status: true,
message: `Hello ${oldUser.first_name}`
})
}else{
return res.status(498).json({
status: false,
message: `Invalid Id`
})
}
} catch (error) {
return res.status(500).json({
status: false,
message: “An error occurred”
})
}
}
Что мы сделали :
1. Получите идентификатор от `req.user`, когда пользователь аутентифицирован
2. Найти пользователя по идентификатору из базы данных
3. Вернуть ответное приветствие пользователя с именем в базе данных.
Создание и использование маршрутов
Следующее, что мы хотим сделать, это создать конечные точки, подключить их к контроллерам в файле `authroute.js` и импортировать их в файл `app.js`, чтобы он был доступен.
Создание и подключение маршрутов в файле `authroute.js`:
import { Router } from “express”;
import { signUp, signIn, greetUser} from “../controller/authcontroller.js”;
import { verifyToken } from “../middleware/auth.js”;
const router = Router();
router.route(“/signup”).post(signUp);
router.route(“/signIn”).post(signIn);
router.route(“/greet-user”).get(verifyToken, greetUser)
export default router;
Импортируйте и используйте маршруты в файле `app.js`:
import authroute from “./routes/authroute.js” app.use(authroute)
Ваш файл `app.js` должен выглядеть так:
import express from ‘express’
import connect from “./config/connect.js”;
import authroute from “./routes/authroute.js”
const app = express()
app.use(
express.urlencoded({
extended: false,
})
);
app.use(express.json());
app.use(authroute)
connect.connectToLocalMongoDB()
export default app
Тестирование конечных точек в Postman
Тестирование регистрации Enpoint
Запрос :

Ответ :

Тестирование входа в систему
Запрос :

Ответ :

Тестирование приветствия пользователя
Вставка JWT в заголовок:

Запрос :

Ответ :

Заключение
Итак, мы зашли так далеко, и я думаю, вы должны были научиться
– Создать JWT
– Использовать JWT в точках регистрации и входа
– Извлечь JWT из заголовка и расшифровать его
– Как использовать объект из JWT, когда пользователь проходит аутентификацию
«Совершенство достигается не тогда, когда нечего добавить, а тогда, когда нечего больше убрать». — Антуан де Сент-Экзюпери
Узнать больше о JWT можно Здесь
Спасибо за прочтение.
Получить полный код можно Здесь
Подпишитесь на меня, чтобы получить больше советов по разработке бэкенда.