Оглавление:
Карта сайта:
Оглавление:
Карта сайта:
Нельзя говорить про AJAX и не упомянуть про важнейшую деталь его реализации – защиту от CSRF-атак.
CSRF (Cross-Site Request Forgery, также XSRF) – опаснейшая атака, которая приводит к тому, что хакер может выполнить на неподготовленном сайте массу различных действий от имени других, зарегистрированных посетителей.
Какие это действия – отправка ли сообщений, перевод денег со счёта на счёт или смена паролей – зависят от сайта, но в любом случае эта атака входит в образовательный минимум веб-разработчика.
server.js
const express = require('express');//Подключаем модуль const path = require('path');//Модуль для работы с путями к файлам и папкам const csrf = require('csurf');//CSRF - защита (Cross-Site Request Forger) const exphbs = require('express-handlebars');//Подключаем шаблонизатор const db = require('mongoose'); //библиотека для mongodb const session = require('express-session');//модуль для создания сессий, в express const MongoStore = require('connect-mongodb-session')(session);//Реквайр возвращает функцию которую мы сразу вызываем, // куда мы передаем пакет session, который используем для синхронизации. Вернет класс const config = require('./config/config'); //конфиг с url к БД, и секретным ключом const User = require('./models/user'); // const varMiddleware = require('./middleware/variables');//модуль проверки авторизации const userMiddleware = require('./middleware/user');//модуль инициализации авторизованного пользователя const app= express();//Результат работы модуля express, по сути создает сервер const PORT = process.env.PORT || 3000; //Получить порт из переменного окружения или использовать дефолтный const store = new MongoStore({//создаем коллекцию в БД collection: 'sessions',//Название коллекции uri: config.mongoURI//коннект }); /** * Подключаем роуты */ const homeRoutes = require('./routes/home');//главная страница const coursesRoutes = require('./routes/courses');//Страница courses const addRoutes = require('./routes/add');//Страница add const aboutRoutes = require('./routes/about');//Страница about const cardRoutes = require('./routes/card');//Страница about const ordersRoutes = require('./routes/orders');//Страница about const authRoutes = require('./routes/auth');//Страница auth const hbs= exphbs.create({//Настройки шаблонизатора defaultLayout: 'main',//Шаблон по умолчанию, основной, (template/layouts/main.hbs) extname: 'hbs' //Расширение шаблонов, пользовательское. По умолчанию express-handlebars }); app.engine('hbs', hbs.engine);//Подключени шаблонизатора к express, регестрируем движок app.set('view engine', 'hbs');//Запускаем шаблонизатор, используем app.set('views', 'template');//Указываем папку с шаблонами app.use(express.static(path.join(__dirname,'public')));//Статические папки для хранения стилей картинок app.use(express.urlencoded({extended: true})); app.use(session({//инициализация сессии secret: 'key secret',//секретный ключ для сессии resave: false, saveUninitialized: false, store: store //передаем объект })); app.use(csrf());//Включение CSRF защиты (Cross-Site Request Forger), подключать нужно после сессии app.use(varMiddleware);//middleware - срабатывает при загруке страницы, данный метод проверяет авторизацию middleware/variables.js app.use(userMiddleware);//middleware - срабатывает при загруке страницы, инициализируем авторизованного пользователя /** * Регестрируем роуты, с префиксами */ app.use('/', homeRoutes);//Главная страница app.use('/courses',coursesRoutes);//страница courses app.use('/add',addRoutes);//страница add app.use('/about',aboutRoutes);//Страница about app.use('/card',cardRoutes);//Страница корзина app.use('/orders',ordersRoutes);//Страница заказов app.use('/auth',authRoutes);//Страница заказов /** * Запуск сервера * @returns {Promise<void>} */ async function start(){ try{ await db.connect(config.mongoURI,{//подключение к БД useNewUrlParser: true, useFindAndModify: false }); console.log('MongoDB has started ...'); app.listen(PORT, () => {//Слушать 3000 порт console.log(`server is running on port ${PORT}`); }); } catch(e){ console.log(e);//Ксли коннект не удался; } } start();
если используется обычная форма, token отправляем скрытым полем
<form action="/auth/login" method="post"> <div class="row"> <div class="input-field col s12"> <input id="email" type="text" name="email" class="validate" required /> <label for="email">email</label> <span class="helper-text" data-error="введите email" data-success="right"></span> </div> <div class="input-field col s12"> <input id="password" type="text" name="password" class="validate" required /> <label for="password">пароль</label> <span class="helper-text" data-error="Введите password" data-success="right"></span> </div> </div> <div class="row"> <div class="input-field col"> <input type="hidden" name="_csrf" value="{{csrf}}"> <input type="submit" class="btn" value="войти"> </div> </div> </form>
public/js/script.js если отправляем форму через ajax, токен отдаем заголовком
const $card = document.querySelector('#card'); if($card){ $card.addEventListener('click', (evt) => { // evt.preventDefault(); if(evt.target.classList.contains('js-remove')){//получить список элементов с классами содержащие класс "js-remove" const id = evt.target.dataset.id; //аналог jquery, $(evt.target).data('id') const csrf = evt.target.dataset.csrf; //аналог jquery, $(evt.target).data('csrf') console.log(evt.target.dataset.csrf); fetch('/card/remove/'+id,{//Ajax запрос замена XMLHttpRequest() method: 'delete',//delete - запрос headers:{'X-XSRF-TOKEN': csrf}//заголовки запроса //body:JSON.stringify({//Тело запроса // _csrf:csrf //}) }).then(res => res.json()) .then(card => {//Возвращает промис console.log(card); if(card.courses.length){ const html= card.courses.map((c)=> { return ` <tr> <td>${c.title}</td> <td>${c.count}</td> <td><a href="#" class="btn"><i class="material-icons js-remove" data-id="${c.id}">delete</i></a></td> </tr> `; }).join(); $card.querySelector('tbody').innerHTML = html; $card.querySelector('.price').textContent = new Intl.NumberFormat('ru-Ru', //локаль { currency: 'rub',//опции style: 'currency' }).format(card.price);//здесь какоето число } else{ $card.innerHTML = `<p>Корзина пуста</p>` } }); } }) }