node_acl用法示例

来源:互联网 发布:java ocr识别 验证码 编辑:程序博客网 时间:2024/06/02 00:27

需要用nodejs实现用户访问控制,有一个node_acl的包可以提供ACL功能,但是没找到什么资料。github上有一个node_acl的demo,翻译一下造福群众。原文地址在这里https://github.com/OptimalBits/node_acl/issues/38
举一个图书的例子,页面路由如下:

/books/books/:bookId/books/:bookId/pages/books/:bookId/pages/:pageId

接下来要考虑每个路径都有哪些行为(action),action有以下四种,都是标准的HTTP查改增删的行为:

get
post
put
delete

这些也是你的权限(permisson)。
角色(role)定义如下:

admin - usually has unlimited access to controlled resourcesuser - has limited access to controlled resourcespublic - has very limited access to controlled resourcesdisabled - has no access to controlled resources

最后是使用者(user),关键是要将user和role对应。当需要检查权限的时候,你只要检查这个user所属的role是否对资源(resource)有权限就行。
(译者注:这里的resource可以理解为对某条路径的访问权限,比如角色admin可以访问/admin路径和/index路径,而角色guest只能访问/index路径)
所以数据结构可以这样设计,一方面要配对roles->resources->permissions,另一方面要配对users->roles

var publicRole = {    name: 'public',    resources: [    ],    permissions: []};var adminRole = {    name: 'admin',    resources: [        '/books',        '/books/:param1',        '/books/:param1/pages',        '/books/:param1/pages/:pageId'    ],    permissions: '*'};var userRole = {    name: 'user',    resources: [        '/books',    ],    permissions: ['get', 'post']};var allRoles = [    publicRole,    adminRole,    userRole];

以及

var users = [    {        username: 'public',        roles: ['public'],        password: 'public'    },    {        username: 'admin',        roles: ['admin'],        password: 'admin_password'    },    {        username: 'foobar',        roles: ['user'],        password: 'barfoo'    }];

我要承认我跳过了一些步骤,所以让人困惑。在resources的定义中,我用了”:”符号,这是一个占位符,但实际上node_acl没有这种用法,它仅仅支持字符串的匹配。为了匹配带参数的路径,我们需要generalize paramaterized paths(词穷了,不知道怎么翻),稍后会讲到这一点。
定义了ACL数组和用户数组之后,需要将它们添加到ACL列表中,大概语法像下面那样:

for each role in allRoles   for each resource in role.resources      node_acl.allow(role.name, resource, role.permissions)for each user in users   create a new User(user.username, user.password) as new user   on new user created      node_acl.addUserRoles(new user.id, user.roles)

(译者注,这里create a new User作者在另一个示例中是用mongodb的entity实现的,另一个示例的链接在本文底部)
这只是一个基础的用法,更高级的用法允许你对资源有更细化的权限。
最后我们来实现access control logic。我们假设用express()框架,用middleware()方法生成的中间件可以很容易将node_acl整合进来

app.get('/books/:bookId', node_acl.middleware(), booksCtrl.getBook)app.put('/books/:bookId', node_acl.middleware(), booksCtrl.editBook)app.delete('/books/:bookId', node_acl.middleware(), booksCtrl.deleteBook)app.get('/books/:bookId/pages', node_acl.middleware(), booksCtrl.listPages)app.post('/books/:bookId/pages', node_acl.middleware(), booksCtrl.addPage)app.get('/books/:bookId/pages/:pageId', node_acl.middleware(), booksCtrl.getPage)app.put('/books/:bookId/pages/:pageId', node_acl.middleware(), booksCtrl.editPage)app.delete('/books/:bookId/pages/:pageId', node_acl.middleware(), booksCtrl.deletePage)

这样就可以自动生成类似如下的代码

func (reqest, response, next) ->   node_acl.isAllowed(request.userId, url, httpMethod, func(error, isAllowed) ->       if (isAllowed) ->          next()      else ->         response.notAllowed()   )

但是我有两点疑问:一个是我原先的应用没有req.userId这个属性,而是req.user.id;还有一个就是我的路径有一些没有显式表明的参数。(笔者注:比如路径中有bookId,你不可能把所有不同bookId的路径都添加到resources里面吧)
所以我不用它自动生成的中间件而是自己写了一个,大约如下:

function myMiddleware(req, res, next) ->    if (req.user is undefined) ->      req.user = { id: 'public' }   id = req.user.id   // here i need to normalize the route in order to ignore routes with parameters   routeParts = req.path.split('/')   for each part in routeParths      if (part matches an id format) ->         replace it with (':param' + counter)   routeParts.join('/')   // this will convert a route /books/abc123 to /books/:param1   // now actually do the acl check  node_acl.isAllowed(id, routeParts, request.method, func(err, isAllowed) ->      if(isAllowed) ->        next();     else        response.notAllowed

译者注:这个例子还是不够完整,作者又给了一个完整的例子 https://github.com/icompuiz/express-mongoose-acl/commit/e27ef6c2bd61623f44849fd676d38bb289bc07eb