node_acl用法示例

需要用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 resources
user - has limited access to controlled resources
public - has very limited access to controlled resources
disabled - 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

你可能感兴趣的:(node_acl用法示例)