Написание простого блога на SailsJS: наглядная практика для начинающих (Часть 2)

от автора

Синопсис

Ранее мы изучили написание основы для нашего блога, при написании основы мы ознакомились с организацией статики, составлением модели и написанием кода контроллера. Узнали как можно работать с конфигурациями путей (routes.js), и как работать с представлениями в SailsJS. Во второй части о написании простого блога на SailsJS, мы рассмотрим следующие пункты: Пользователи: создание. Сессии: создание (вход), разрыв (выход). Написание Админ Панели, и работа с политикой и ограничениями доступа.

Пользователи

Для создания нового комплекса API вводим уже знакомую нам команду в корне нашего проекта.

sails generate api user 

В этот раз при организации кода нам понадобится шифровать пароль, для этого нам потребуется отличный модуль password-hash, который подойдет для этой задачи. Чтобы его установить — в корне нашего проекта введите следующую команду

npm install password-hash --save 

Параметр —save указывает на то что мы сохраним значение модуль как зависимость в package.json.

Так как в предыдущем посте я уже рассмотрел базовые навыки работы с моделями и контроллерами в SailsJS — где они находятся, и как их правильно составлять, я не буду обращать внимания на уже очевидные вещи.

Модель

Для нашего пользователя будет несколько атрибутов:

  1. Имя пользователя
  2. Пароль
  3. Админ — параметр доступа

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

var passwordHash = require('password-hash');  var User = {    attributes: {  	username: {type: 'string', required: true, unique: true}, 	password: {type: 'string', required: true, minLength: 8},      admin: {       type: 'boolean',       defaultsTo: false     },     toJSON: function() {     	var element = this.toObject();     	delete element.password;     	return element;     }    },    beforeCreate: function (values, next) {     // Создаем зашифрованную запись пароля в БД     var mainPass = passwordHash.generate(values.password);     values.encryptPassword = mainPass;     next();  	   }  };  module.exports = User;  

До создания нового пользователя мы добавляем дополнительный атрибут — encryptedPassword, который представляет собой зашифрованную версию пароля.

Контроллер

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

 module.exports = {  	//@API - создание пользователя  	/** 	 * Создание нового пользователя, 	 * в качестве параметров передаем 	 * имя пользователя, пароль, и булевое 	 * значение админ. После создания 	 * пользователя он аутентифицируется 	 * в сессии. После создания пользователя  	 * администратора мы установим политику 	 * admin (api/policies/admin.js) чтобы к 	 * этой функции больше не могли обращаться 	 * не привелегированные пользователи 	 */ 	  	create: function (req, res) { 		var elem = { 			username : req.param('username'), 			password : req.param('password'), 			admin    : req.param('admin') 		};  		User.create(elem).exec(function (err, user) { 			if (err) return res.send(500); 			req.session.auth = true; 			res.redirect('/'); 		}); 	},  	// @MAIN 	index: function (req, res) { 		res.view(); 	} };  

В конфигурации путей (config/routes.js) добавим следующее.

  '/register' : 'UserController', 

В качестве простой защиты включим защиту CSRF в файле конфигурации (config/csrf.js) отредактируем строку следующим образом

module.exports.csrf = true; 
Представление (views/user/index.ejs)

А теперь составим представление, оно будет иметь простейшую структуру.

<div class="container"> 	<div class="row"> 		<div class="col-md-4"></div> 		<div class="col-md-4"> 			<div class="panel panel-default"> 				<div class="panel-body"> 					<form action="/user/create" method="post"> 						<input type="text" name="username" placeholder="Имя Пользователя"><br> 						<input type="password" name="password" placeholder="Пароль"><br> 						<input type="hidden" name="_csrf" value="<%= _csrf %>"> 						<!-- Поле которое можно удалить после создания администратора --> 						<input type="hidden" name="admin" value="true"> 						 						<input type="submit" class="btn btn-success btn-block" value="Зарегистрироваться"> 					</form> 				</div> 			</div> 		</div> 		<div class="col-md-4"></div> 	</div> </div> 

Или же вместо представления можно просто отключив csrf создать пользователя — например с помощью Postman. А потом заблокировать контроллер Пользователей.

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

Сессии

Для авторизации пользователей мы будем использовать встроенную в Sails систему сессий — это удобно и достаточно безопасно (если сравнивать с cookie) наш контроллер сессий сможет создавать сессии и уничтожать их. Создайте новый контроллер стандартным способом. Вот код нашего контроллера.

var passwordHash = require('password-hash');  module.exports = {   	// @API основные функции сессии  	create: function (req, res) { 		/** 		 * Задаем переменные запрашиваемых 		 * параметров, в нашем случае логин 		 * и пароль 		 */ 		var username = req.param('username'), 			password = req.param('password');  		/** 		 * Если нет логина или пароля в запросе 		 * вывести ошибку, и перенаправить обратно 		 * (прим. здесь лучше сделать подробную 		 * обработку ошибок, например с flash) 		 */  		if (!username || !password) { 			return res.redirect('/session'); 		};  		/** 		 * Найти пользователя из запроса логина 		 * (username - req.param('username')) 		 * когда пользователь найден производиться  		 * сравнение зашифрованного пароля с паролем 		 * который был отправлен запросом, если он 		 * валиден, то создается внешний статус -  		 * авторизован или нет, и дается доступ к 		 * данным через внешний доступ сессии. Это 		 * позволит нам в дальнейшем создать политику 		 * для ограничивания доступа к определенным  		 * разделам нашего блога (используя сессии) 		 */ 		User.findOneByUsername(username).exec(function (err, user) { 			if (!user || err) return res.send(500); 			if (passwordHash.verify(password, user.encryptPassword)) { 				// Авторизовать пользователя в сессии                                 // Дать доступ к данным авторизованного                                  // пользователя из сессии 				req.session.auth = true; 				req.session.User = user; 				 				if (req.session.User.admin) { 					return res.redirect('/admin'); 				}; 			}; 		}); 	}, 	/** 	 * Создаем выход из сессии который  	 * просматривает есть ли пользователь 	 * в онлайне, и уничтожает сессию 	 */ 	destroy: function (req, res) { 		User.findOne(req.session.User.id).exec(function (err, user) { 			if (user) { 				req.session.destroy(); 				res.redirect('/'); 			} else { res.redirect('/login'); }; 		}); 	},  	// @MAIN  	index: function (req, res) { 		res.view(); 	} }; 
Конфигурация путей

  '/login'    : 'SessionController',   '/logout'   : {     controller: 'session',     action: 'destroy'   }, 
Представление

И представление страницы входа

<div class="container"> 	<div class="row"> 		<div class="col-md-4"></div> 		<div class="col-md-4 text-center"> 			<h2>Sign-in form</h2><hr> 			<form action="/session/create" method="POST"> 				<input type="text" name="username" placeholder="Username" class="form-control" /><br> 				<input type="password" name="password" placeholder="password" class="form-control" /><br> 				<input type="submit" class="btn btn-default" value="Log-In" /> 				<input type="hidden" name="_csrf" value="<%= _csrf %>" /> 			</form> 		</div> 		<div class="col-md-4"></div> 	</div> </div> 

Теперь мы создали возможность регистрироваться и создавать сессии (входить с систему), теперь осталось сделать страницу админ панельки, и настроить политику разграничения доступа. И так завершающий этап.

Админ Панель и Разграничение Прав

У нас будет простейшая страница админки состоящая из списка постов, и формы добавления нового поста. А также страница для редактирования уже существующих постов. Теперь создайте новый контроллер admin. Вот код нашего контроллера.

module.exports = { 	 	index: function (req, res) { 		Post.find() 			.sort('id DESC') 			.exec(function (err, posts) { 				if (err) return res.send(500); 				res.view({ 					posts: posts 				}); 			}); 	},  	edit: function (req, res) { 		var Id = req.param('id');  		Post.findOne(Id).exec(function (err, post) { 			if (!post) return res.send(404); 			if (err) return res.send(500); 			res.view({ 				post: post 			}); 		}); 	} }; 

И 2 наших представления.

views/admin/index.ejs

<div class="container text-center"> 	<h2>CREATE NEW</h2><br> 	<div class="row"> 		<form action="/post/create" method="POST"> 			<div class="col-md-6"> 				<input class="form-control" type="text" name="title" placeholder="Title Post"><hr> 				<textarea rows="3" class="form-control" name="description" placeholder="Description"></textarea> 			</div> 			<div class="col-md-6"> 				<textarea rows="7" class="form-control" name="content" placeholder="Content"></textarea> 			</div> 			<div class="col-md-12"> 				<input type="hidden" name="_csrf" value="<%= _csrf %>" /> 				<br><input type="submit" class="btn btn-success" value="CREATE" /> 			</div> 		</form> 	</div><br> 	<h2>POST LIST</h2> 	<table class="table text-left"> 		<tr> 			<th>ID</th> 			<th>TITLE</th> 			<th></th> 			<th></th> 			<th></th> 		</tr> 		<% _.each(posts, function (post) { %> 			<tr> 				<td><%= post.id %></td> 				<td><%= post.title %></td> 				<td><a href="/post/watch/<%= post.id %>" class="btn btn-info">Look</a></td> 				<td><a href="/post/delete/<%= post.id %>" class="btn btn-danger">Delete</a></td> 				<td><a href="/admin/edit/<%= post.id %>" class="btn btn-warning">Edit</a></td> 			</tr>			 		<% }) %> 	</table> </div> 
views/admin/edit.ejs

<div class="container"> 	<div class="row"> 		<div class="col-md-2"></div> 		<div class="col-md-8 well text-center"> 			<form action="/post/update" method="POST"> 				<h4>Title</h4> 				<input type="text" class="form-control" name="title" value="<%= post.title %>"><br> 				<h4>Description</h4> 				<textarea rows="3" name="description" class="form-control" value="<%= post.description %>"></textarea> 				<h4>Content</h4> 				<textarea rows="7" name="content" class="form-control" value="<%= post.content %>"></textarea> 				<input type="hidden" name="_csrf" value="<%= _csrf %>" /><br> 				<input type="submit" class="btn btn-success" value="Update"> 			</form> 		</div> 		<div class="col-md-2"></div> 	</div> </div> 

А теперь займемся созданием политики.

Политика

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

  • Был Авторизован
  • И являлся администратором

Чтобы выразить это в коде создадим файл нашей политики — api/policies/admin.js. И вот код нашего разграничителя.

module.exports = function (req, res, ok) { 	if (req.session.auth && req.session.User.admin) { 		return ok(); 	} else { 		return res.redirect('/login'); 	}; } 

В данном случае коллбек возвращается для того чтобы пропустить дальнейшие действия. если результат противоположный — запретить и перенаправить на страницу входа. Для активации нашей политики на определенном контроллере — откройте файл config/policies.js и приведите его в следующий вид.

 module.exports.policies = {    // Default policy for all controllers and actions   // (`true` allows public access)   '*': true,    /**    * Вставляем для нашего контроллера    * Admin политику admin.js, которая    * ограничивает доступ.    */    AdminController: {   	'*': 'admin'   },    UserController: {   	create: 'admin'   },    PostController: {   	// То что могут видеть все   	index  : true,   	page   : true,   	watch  : true,    	// То что может только админ   	create : 'admin',   	update : 'admin',   	delete : 'admin',   } }; 

На этом мы закончим написание простого блога на Sails, конечно в нем очень мало функционала и защиты — нет обработки ошибок (даже flash сообщений), нет полноценной мультипользовательской админки, но эта статья расcчитана как маленький вводный курс в этот фреймворк, дальше можете изучать его сами. В дальнейшем нарастить функционал вам не должно составить большого труда.

ссылка на оригинал статьи http://habrahabr.ru/post/224403/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *