DevGang
Авторизоваться

Обработка аутентификации в Express.js 

В этой статье мы собираемся сделать простое приложение, чтобы продемонстрировать, как вы можете обрабатывать аутентификацию в Express.js. Поскольку мы будем использовать некоторые базовые синтаксисы ES6 и платформу Bootstrap для разработки пользовательского интерфейса, это может помочь, если у вас есть базовые знания об этих технологиях.

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

Настройка проекта

Во-первых, давайте создадим новую папку с именем, скажем, simple-web-app. Используя терминал, мы перейдем к этой папке и создадим каркасный проект Node.js:

npm init

Теперь мы также можем установить Express:

npm install --save express

Для простоты мы будем использовать серверный движок рендеринга под названием Handlebars. Этот движок будет отображать наши HTML-страницы на стороне сервера, поэтому нам не понадобятся никакие другие передовые фреймворки, такие как Angular или React.

Давайте продолжим и установим express-handlebars:

npm install --save express-handlebars

Мы также будем использовать два других промежуточных пакета Express (body-parser и cookie-parser) для анализа тела HTTP-запросов и анализа необходимых файлов cookie для аутентификации:

npm install --save body-parser cookie-parser

Реализация

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

Для начала давайте импортируем библиотеки, которые мы ранее установили:

const express = require('express');
const exphbs = require('express-handlebars');
const cookieParser = require('cookie-parser');
const bodyParser = require('body-parser');

Мы будем использовать собственный модуль crypto для хеширования паролей и генерации токена аутентификации - это будет подробно описано чуть позже в этой статье.

Далее, давайте создадим простое приложение Express и сконфигурируем промежуточное ПО, которое мы импортировали, вместе с движком Handlebars:

const app = express();

app.use(bodyParser.urlencoded({ extended: true }));
app.use(cookieParser());

app.engine('hbs', exphbs({
    extname: '.hbs'
}));

app.set('view engine', 'hbs');

app.listen(3000);

По умолчанию в Handlebars расширение шаблона должно быть .handlebars. Как вы можете видеть в этом коде, мы настроили наш шаблонизатор для поддержки файлов с более коротким расширением .hbs. Теперь давайте создадим несколько файлов шаблонов:

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

Давайте создадим нашу главную страницу main.hbs:



    
        
        
        
        Document

        
    
    

        
{{{body}}}

Другие шаблоны будут отображаться внутри тега {{{body}}}. У нас есть шаблон HTML и необходимые файлы CSS и JS для Bootstrap, импортированные в этот макет.

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

Затем давайте создадим обработчик запроса к пути root path (/) для визуализации шаблона home.

app.get('/', function (req, res) {
    res.render('home');
});

Давайте запустим наше приложение и перейдем к http://localhost:3000:

Регистрация аккаунта

Информация об учетной записи собирается через страницу registration.hbs:

{{#if message}} {{/if}}

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

Давайте создадим дескриптор запроса для отображения шаблона регистрации при посещении пользователя http://localhost:3000/register:

app.get('/register', (req, res) => {
    res.render('register');
});

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

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

const crypto = require('crypto');

const getHashedPassword = (password) => {
    const sha256 = crypto.createHash('sha256');
    const hash = sha256.update(password).digest('base64');
    return hash;
}

Когда пользователь отправляет регистрационную форму, POST запрос будет отправлен на путь /register.

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

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

const users = [
    // Этот пользователь добавляется в массив, чтобы избежать создания нового пользователя при каждом перезапуске
    {
        firstName: 'John',
        lastName: 'Doe',
        email: 'johndoe@email.com',
        // Это хэш SHA256 для значения `password`
        password: 'XohImNooBHFR0OVvjcYpJ3NgPQ1qq73WKhHvch0VQtg='
    }
];

app.post('/register', (req, res) => {
    const { email, firstName, lastName, password, confirmPassword } = req.body;

    // Проверьте, совпадают ли пароль
    if (password === confirmPassword) {

        // Проверьте, зарегистрирован ли пользователь с тем же адресом электронной почты
        if (users.find(user => user.email === email)) {

            res.render('register', {
                message: 'User already registered.',
                messageClass: 'alert-danger'
            });

            return;
        }

        const hashedPassword = getHashedPassword(password);

        // Сохранить пользователя в базе данных, если вы его используете
        users.push({
            firstName,
            lastName,
            email,
            password: hashedPassword
        });

        res.render('login', {
            message: 'Registration Complete. Please login to continue.',
            messageClass: 'alert-success'
        });
    } else {
        res.render('register', {
            message: 'Password does not match.',
            messageClass: 'alert-danger'
        });
    }
});

Принимаемые emailfirstNamelastName,password, и confirmPassword проверяются - пароли совпадают, адрес электронной почты не зарегистрирован и т.д.

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

Теперь давайте посетим страницу /register, чтобы проверить, что она работает правильно:

Логин аккаунта

Теперь мы можем реализовать функционал входа в систему. Давайте начнем с создания страницы login.hbs:

{{#if message}} {{/if}}

А затем давайте создадим обработчик для этого запроса:

app.get('/login', (req, res) => {
    res.render('login');
});

Эта форма будет отправлять POST запрос на /login, когда пользователь отправляет форму. Тем не менее, еще одна вещь, которую мы будем делать, это отправка токена аутентификации для входа в систему. Этот токен будет использоваться для идентификации пользователя, и каждый раз, когда он отправляет HTTP-запрос, этот токен будет отправляться с помощью cookie:

const generateAuthToken = () => {
    return crypto.randomBytes(30).toString('hex');
}

С помощью нашего вспомогательного метода мы можем создать обработчик запросов для страницы входа в систему:

// Тут будут хранится пользователи и authToken, связанных с ними
const authTokens = {};

app.post('/login', (req, res) => {
    const { email, password } = req.body;
    const hashedPassword = getHashedPassword(password);

    const user = users.find(u => {
        return u.email === email && hashedPassword === u.password
    });

    if (user) {
        const authToken = generateAuthToken();

        // Сохранить токен аутентификации
        authTokens[authToken] = user;

        // Установка токена авторизации в куки
        res.cookie('AuthToken', authToken);

        // Перенаправить пользователя на защищенную страницу
        res.redirect('/protected');
    } else {
        res.render('login', {
            message: 'Invalid username or password',
            messageClass: 'alert-danger'
        });
    }
});

В этом обработчике запросов authTokens используется для хранения токенов аутентификации в качестве ключа и соответствующего пользователя в качестве значения, что позволяет использовать простой токен для поиска пользователя. Вы можете использовать базу данных, такую ​​как Redis, или любую другую базу данных для хранения этих токенов - мы используем этот объект для простоты.

Давайте перейдем на /login:

Мы еще не совсем закончили. Нам нужно авторизовать пользователя, прочитав из cookie authToken после получения запроса. Над всеми обработчиками запросов и под cookie-parser промежуточным программным обеспечением давайте создадим наше собственное промежуточное программное обеспечение для внедрения пользователей в запросы:

app.use((req, res, next) => {
    // Получение значения из cookie
    const authToken = req.cookies['AuthToken'];

    // Добавление авторизованного пользователя в запрос
    req.user = authTokens[authToken];

    next();
});

Теперь мы можем использовать req.user внутри наших обработчиков запросов, чтобы проверить, аутентифицирован ли пользователь с помощью токена.

Наконец, давайте создадим обработчик запросов для отображения защищенной страницы protected.hbs:



This page is only visible to logged in users

И обработчик запроса для страницы:

app.get('/protected', (req, res) => {
    if (req.user) {
        res.render('protected');
    } else {
        res.render('login', {
            message: 'Please login to continue',
            messageClass: 'alert-danger'
        });
    }
});

Как видите, вы можете использовать, req.user чтобы проверить, аутентифицирован ли пользователь. Если этот объект пуст, пользователь не аутентифицирован.

Другой способ требовать аутентификации на маршрутах - это реализовать его в качестве промежуточного программного обеспечения, которое затем можно применить к маршрутам напрямую, как они определены в объекте app:

const requireAuth = (req, res, next) => {
    if (req.user) {
        next();
    } else {
        res.render('login', {
            message: 'Please login to continue',
            messageClass: 'alert-danger'
        });
    }
};

app.get('/protected', requireAuth, (req, res) => {
    res.render('protected');
});

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

Вывод

Аутентификация пользователя в Express довольно проста и понятна. Мы использовали модуль crypto для хэширования паролей зарегистрированных пользователей в качестве базовой функции безопасности и создали защищенную страницу, видимую только пользователям, аутентифицированным с помощью токена.

Исходный код этого проекта можно найти на GitHub

Источник:

#JavaScript #NodeJS #Express
Комментарии
Чтобы оставить комментарий, необходимо авторизоваться

Присоединяйся в тусовку

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

Попробовать

Оплатив хостинг 25$ в подарок вы получите 100$ на счет

Получить