环回授权用户只能查看其数据 [英] Loopback authorize a user to see only his data
问题描述
我正在使用Loopback开发NodeJS应用程序.
I am developing a NodeJS application using Loopback.
我对nodejs和REST API都还很陌生,所以如果我在概念上错了,请纠正我.
I am pretty new to both nodejs and REST APIs, so please correct me if I am conceptually wrong.
Loopback自动构建CRUD REST API,这是我要使用的功能,以避免自己编写API,但是我需要限制用户只能查看其数据.
Loopback automatically builds CRUD REST APIs, which is a feature I would like to use in order to avoid to write APIs by myself, but I need to limit users to be able to see only their data.
例如,假设我的数据库中有3个表,分别是user
,book
和一个关系表user_book
.
For example, imagine there are 3 tables in my DB, user
, book
and a relation table user_book
.
例如:
table user
id | name
---------
1 | user1
2 | user2
3 | user3
table book
id | title | author
-------------------
1 | title1 | author1
2 | title2 | author1
3 | title3 | author2
4 | title4 | author2
5 | title5 | author3
table user_book
id | user_id | book_id
-------------------
1 | 1 | 1
2 | 1 | 4
3 | 1 | 3
4 | 2 | 3
5 | 2 | 2
6 | 2 | 1
7 | 3 | 3
对用户X
进行身份验证后,API /books
应该仅回答X的书籍,而不是表格中的每本书籍.例如,如果用户user1
被登录并调用/books
,则他获取的应该仅是他的书,因此ID为1, 3, 4
的书.
When a user X
is authenticated, the API /books
should answer with ONLY X's books, and not every book in the table. For example, if user user1
is logged and calls /books
, he get should only get his books, so books with id 1, 3, 4
.
类似地,/books?filter[where][book_author]='author1'
应该只返回用户X
的作者为"author1"的图书.
Similarly, /books?filter[where][book_author]='author1'
should return only books of user X
whose author is 'author1'.
我发现环回提供了远程钩子在执行远程方法之前和之后进行附加,并且还提供所谓的
I found out that loopback offers remote hooks to attach before and after the execution of a remote method, and also offers so called scopes to
[...]指定可以用作方法调用引用的常用查询 在模型上[...]
[...]specify commonly-used queries that you can reference as method calls on a model[...]
我正在考虑使用两者的组合,以便将对表books
的访问限制为仅运行该调用API的用户行.
I was thinking about using a combination of the 2 in order to limit access to the table books
to only rows of the user that runs calls the API.
module.exports = function (book) {
// before every operation on table book
book.beforeRemote('**', function (ctx, user, next) {
[HERE I WOULD PERFORM A QUERY TO FIND THE BOOKS ASSOCIATED WITH THE USER, LET'S CALL ID book_list]
ctx._ds = book.defaultScope; // save the default scope
book.defaultScope = function () {
return {
'where': {
id in book_list
}
};
};
next();
});
book.afterRemote('**', function (ctx, user, next) {
book.defaultScope = ctx._ds; // restore the default scope
next();
});
};
此解决方案行得通吗?我尤其特别关注并发性.如果有来自不同用户的/books
多次请求,那么更改默认范围将是一项关键操作吗?
Would this solution work? In particular, I am particularly concerned about concurrency. If multiple requests happen for /books
from different users, would changing the default scope be a critical operation?
推荐答案
我们完成此操作的方法是创建一个mixin.看看github中的回送时间戳混合.我建议混合创建与您的用户模型的所有者"关系.简而言之,这是它的工作方式:
The way we accomplished this was to create a mixin. Have a look at the loopback timestamp mixing in github. I would recommend the mixing create an "owner" relation to your user model. Here's how it works in a nutshell:
- 每个使用mixin的模型都会在模型和用户之间创建一个关系
- 每次创建模型的新实例时,userId都会与该实例一起保存 每次调用
- find 或 findById 时,都会对查询进行修改以添加 {其中:{userId:[当前登录的用户ID]}} 条款
- Each model that uses the mixin will have a relation created between the model and the user
- Every time a new instance of the model is created, the userId will be saved with the instance
- Everytime find or findById is called, the query will be amended to add the {where:{userId:[currently logged in user id]}} clause
/common/mixins/owner.js
'use strict';
module.exports = function(Model, options) {
// get the user model
var User = Model.getDataSource().models.User;
// create relation to the User model and call it owner
Model.belongsTo(User, {as: 'owner', foreignKey: 'ownerId'});
// each time your model instance is saved, make sure the current user is set as the owner
// need to do this for upsers too (code not here)
Model.observe('before save', (ctx, next)=>{
var instanceOrData = ctx.data ? 'data' : 'instance';
ctx[instanceOrData].ownerId = ctx.options.accessToken.userId;
});
// each time your model is accessed, add a where-clause to filter by the current user
Model.observe('access', (ctx, next)=>{
const userId = safeGet(ctx, 'options.accessToken.userId');
if (!userId) return next(); // no access token, internal or test request;
var userIdClause = {userId: userId};
// this part is tricky because you may need to add
// the userId filter to an existing where-clause
ctx.query = ctx.query || {};
if (ctx.query.where) {
if (ctx.query.where.and) {
if (!ctx.query.where.and.some((andClause)=>{
return andClause.hasOwnProperty('userId');
})) {
ctx.query.where.and.push(userIdClause);
}
} else {
if (!ctx.query.where.userId) {
var tmpWhere = ctx.query.where;
ctx.query.where = {};
ctx.query.where.and = [tmpWhere, userIdClause];
}
}
} else {
ctx.query.where = userIdClause;
}
next();
});
};
/common/models/book.json
{
"mixins": {
"Owner": true
}
}
每次使用所有者混合时,该模型都会在每次创建或保存新实例时自动添加并填充ownerId属性,并且每次获取"数据时都会自动过滤结果.
Every time you use the Owner mixing, that model will automatically have a ownerId property added and filled each time a new instance is created or saved and the results will automatically be filtered each time you "get" the data.
这篇关于环回授权用户只能查看其数据的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!