Серверная часть. Регистрация и авторизация
Для дальнейшего развития редактора необходимо создание серверной части приложения. Для разработки серверной части я использовал 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