Серверная часть. Регистрация и авторизация

2+

Для дальнейшего развития редактора необходимо создание серверной части приложения. Для разработки серверной части я использовал Node.js, Express и базу данных MongoDB.

Первым шагом реализации стало создание регистрации и авторизации на сайте.

Реализация довольно проста. Стартовый файл подключает middlewares и подключается к базе данных.

const express = require('express');
const config = require('config');
const mongoose = require('mongoose');
const cookieParser = require('cookie-parser');
const path = require('path');

const app = express();
app.use(cookieParser());
app.use(express.json({ extended: true }));
app.use('/api/check', require('./routes/checkAuth.routes'));
app.use('/api/auth', require('./routes/auth.routes'));

const PORT = config.get('port');

if (process.env.NODE_ENV === 'production') {
    app.use('/', express.static(path.join(__dirname, 'client')))
  
    app.get('*', (req, res) => {
      res.sendFile(path.resolve(__dirname, 'client', 'index.html'))
    })
  }

async function start() {
    try {
        await mongoose.connect(config.get('mongoURI'), {
            useNewUrlParser: true,
            useUnifiedTopology: true,
            useCreateIndex: true
            })
        app.listen(PORT, () => console.log(`Server has been started at ${PORT}`))
    } catch (error) {
        console.log('Server error', error.message);
        process.exit(1);
    }
}

start()

 Создал роуты с эндпойнтами /register’, ‘/login’ и ‘/logout’/.

Для проверки правильности заполнения полей формы использую библиотеку ‘express-validator’, она добавляется в роутер как middleware.

Для шифрования пароля использовал библиотеку ‘bcryptjs’, для создания токена библиотеку ‘jsonwebtoken’. Токен на клиенте храню в cookies, с флагом “ httpOnly ”, чтобы не было доступа к нему из Javascript.

const { Router } = require('express');
const router = Router();
const config = require('config');
const User = require('../models/User');
const bcrypt = require('bcryptjs');
const jwt = require('jsonwebtoken');
const {check, validationResult} = require('express-validator');

router.post('/register',
    [
        check('name', 'Enter name').exists(),
        check('email', "Incorrect email").isEmail(),
        check('password', 'Min password length is 6 symbols').isLength({ min: 6 })
    ],
    async (req, res) => {
    try {
        const errors = validationResult(req);
        if(!errors.isEmpty()){
            return res.status(400).json({
                errors: errors.array(),
                 message: 'Incorrect registration data'
            })
        }
        const {name, email, password} = req.body;
        const candidate = await User.findOne({ email })

        if (candidate) {
            return res.status(400).json({ message: 'User is already exist' })
        }

        const hashedPassword = await bcrypt.hash(password, 12);
        const user = new User({ name, email, password: hashedPassword });
        await user.save();
        res.status(201).json({ message: 'Account created!' });
    } catch (error) {
        res.status(500).json({ message: "ERROR"})
        console.log(error);
    }
})

router.post(
    '/login',
    [
        check('email', "Incorrect email").normalizeEmail().isEmail(),
        check('password', 'Enter password').exists()
    ],
     async (req, res) => {
         try {
            const errors = validationResult(req);
            if(!errors.isEmpty()){
                return res.status(400).json({
                    errors: errors.array(),
                     message: 'Incorrect registration data'
                })
            }
            const {email, password} = req.body;
            const user = await User.findOne({email});

            if(!user){
                return res.status(400).json({message: 'User not found'});
            }

            const isMatch = await bcrypt.compare(password, user.password);

            if(!isMatch){
                return res.status(400).json({message: 'Wrong password'});
            }

            const token = jwt.sign(
                {userId: user.id},
                config.get('jwtSecret'),
                {expiresIn: '1h'}
            )

            res.clearCookie('token');
            res.cookie('token', token, { httpOnly: true });

            res.json( { userId: user.id, userName: user.name });
             
         } catch (error) {
            res.status(500).json({ message: "ERROR"})
            console.log(error);
             
         }
})

router.post(
    '/logout',
     async (req, res) => {
         try {
            res.clearCookie('token');
            res.json( { userId: undefined, userName: undefined });
        } catch (error) {
            res.status(500).json({ message: "ERROR"})
        }
})

module.exports = router;

При загрузке приложения выполняется проверка, зарегистрирован ли пользователь.

const { Router } = require('express');
const router = Router();
const config = require('config');
const User = require('../models/User');
const jwt = require('jsonwebtoken');

router.post('/checkAuth',
async (req, res) =>{
    try {
        const token = req.cookies.token;
        if (!token) {
            return res.status(401).json({ message: 'No authorization' })
        }

        const decoded = jwt.verify(token, config.get('jwtSecret'));
        if(!decoded.userId){
            res.clearCookie('token');
            return res.status(400).json({message: 'User not found'});
        }
        const userId = decoded.userId;

        const user = await User.findOne({_id: userId});

        if(!user){
            res.clearCookie('token');
            return res.status(400).json({message: 'User not found'});
        }
        res.status(200).json( {userId, userName: user.name});

    } catch (error) {
        res.status(500).json({ message: "ERROR"})
        console.log(error);
    }

    req.cookies
})

module.exports = router;

Полностью серверную часть можно посмотреть на https://github.com/Dragon3DGraff/TertiusAxis_server

2+