...

воскресенье, 29 июня 2014 г.

Написание простого блога на 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читана как маленький вводный курс в этот фреймворк, дальше можете изучать его сами. В дальнейшем нарастить функционал вам не должно составить большого труда.

This entry passed through the Full-Text RSS service — if this is your content and you're reading it on someone else's site, please read the FAQ at http://ift.tt/jcXqJW.


Комментариев нет:

Отправить комментарий