Я уже проделал эту работу, но я хотел бы знать, можно ли считать это хорошим способом.
Мой сервер используется веб-приложением, используемым 3 различными типами пользователей: admin
, shop
, client
. Каждая роль может видеть определенные c вещи и выполнять определенные c API (маршруты). Таким образом, у меня есть 3 проблемы с сетью:
- Role Perms : пользователи могут выполнять (или не могут) определенные c маршруты в соответствии с их
roles
- Доступ к ресурсам : пользователи могут видеть некоторые экземпляры ресурса (или не могут) в зависимости от того, кем он является. Например, клиенты могут видеть только магазины, расположенные на расстоянии не более 5 км.
- Прогноз ресурса : пользователи могут видеть только некоторые свойства ресурса в соответствии с их ролью
Аутентификация управляется с помощью токенов JWT, поэтому пользователь отправляет свой токен на каждый запрос. Сервер проверяет токен и, если он исправен, проверяет, что запрашиваемый API и ресурс доступны для этого пользователя. Ресурсы указываются с использованием моделей Mon goose.
=== 1. Роль Perms
Чтобы проверить авторизацию для API, у меня есть таблица, которая указывает для каждой функции (только те, которые представлены как API), какие пользователи могут запрашивать ее.
auth_table['getItem()'] : {
read: ['admin', 'shop', 'client'],
write:['client', 'admin']
};
И упомянутый маршрут:
routes.get('/user/:user_id/shop/:shop_id/item/:item_id', function(req, res)
{
// it's a GET, so I want to 'read'
var user_id = req.params.user_id;
var shop_id = req.params.shop_id;
var item_id = req.params.item_id;
var data = checkAuthorization(user_id, shop_id, 'getItem()', 'read');
if(data.is_auth)
{
getItem(data.user, data.shop, data.item); /* goes on... */
}
});
Функция checkAuthorization()
находит пользователя, извлекает его роль и проверяет, имеет ли эта роль разрешение «чтение» для функции «getItem». Кроме того, в этом методе, если perm
верно, я добавил логи c для доступа к ресурсам.
Пример , пользователь Даниэле хочет увидеть элемент 001 магазина Fiat поэтому он вызывает маршрут /user/daniele/shop/fiat/item/001
Сервер:
- проверяет токен, прикрепленный к заголовку req
- проверяет роль пользователя (' в этом случае)
- проверьте, имеет ли роль
client
разрешение read
для функции getItem()
function checkAuthorization(user_id, shop_id, function_name, asked_perm)
{
var user = User.findById(user_id); // it's async in reality
var is_auth = auth_table[function_name][asked_perm].contains(user.role);
}
=== 2. Ресурсы Доступ
После шага 3 выше, у меня есть шаг 4:
проверить, может ли клиент Даниэле увидеть магазин Fiat (внутренняя бизнес-логика c)
Так что в функции checkAuthorization()
, после проверки функции perms
, я проверяю, может ли этот клиент увидеть этот магазин. Итак, я сделаю что-то похожее на это:
// if(is_auth)
var shop = Shop.findById(shop_id); // it's async in reality
if( distanceBetween(shop.location, user.location) < 5km)
{ has_resource_access = true; }. // ok user can see items of that shop
Так как в checkAuthorization()
я всегда выполняю User.find (), Shop.find () ... Я решил оставить этот Mon goose объекты, и передавать их среди других функций, вместо того, чтобы каждый раз запрашивать у БД ресурсы. Итак, функция checkAuthorization()
вернет
function checkAuthorization(user_id, shop_id, function_name, asked_perm)
{
// if(is_auth) check Role Perms
// if(has_resource_access ) check Resources Access
return {user:user, shop:shop, is_auth:is_auth}
}
Поэтому я запрашиваю эти основные ресурсы только в первый раз, при проверке подлинности, и я беру с собой эти ресурсы во всех следующих вызываемых подфункциях. Я делаю это для каждого запроса API, думая, что в 99% случаев я должен выполнить User.find () и Shop.find (), поэтому всегда делайте это в начале, так что я могу сохранить весь остальной код чистым и легким, предполагая, что у меня уже есть необходимые ресурсы.
=== 3. Прогнозирование ресурсов
Потому что из этого, то есть я спрашиваю ресурсы БД в начале, я должен спросить их в соответствии с ролью пользователя. Поэтому я решил указать различные проекции в зависимости от роли в модели Mon goose и использовать пользовательскую функцию для поиска документов. Примерно так:
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var ShopSchema = new Schema({
name : {type:String}, // public
money_earned : {type:String}, // just to admin and shop
items : [ {type: item_schema} ],// public
platform_paid: {type:boolean} // admin
});
var model = mongoose.model('Shop', ShopSchema);
var client_projection =
{
'name' : 1,
'items': 1
}
/* I will use this function in my code, not the native 'find()' */
function model.custom_findById(shop_id, role)
{
var projection;
switch (role)
{
case shop: projection = shop_projection; break;
case client: projection = client_projection; break;
case admin: projection = admin_projection; break;
}
return Shop.findById(shop_id, projection);
}
Резюме: если пользователь запрашивает элемент, я должен:
- проверить, может ли роль пользователя читать элементы
- если да, проверьте, может ли этот пользователь видеть этот магазин (и, следовательно, его товары)
- , если да, вернуть документы, скрывая некоторые свойства.
Звучит хорошо