node_acl権限管理パスパスパスパスパスパス

26603 ワード

最近nodejsベースの権限管理をして、1、2日調べてみると、passportjsnode-oauthrbacnode_aclexpress_aclconnect-roles
需要
  • モジュール、ページ、APIなどのレベルに従って権限制御を行い、一時的にボタンレベル
  • を行う必要はない.
  • 主なプログラムの開発が完了し、侵入が少ない
  • が必要である.
  • ストレージは主にredis
  • を考慮する.
  • 自分で管理ページを開発し、
  • のカスタマイズとメンテナンスを容易にする.
    選択の原則
  • 軽量クラスpassportjsは強すぎて、
  • を恐れるほど大きい.
  • ドキュメントクリア(例|API)node_aclのreadme私はただ、本当にとても友好的で、APIも多くありませんが、すべてとてもgetして
  • に着きました.
  • 好手
  • 容易拡張
  • 機能が強力または適用可能
  • コード侵入が少なくコードの修正が嫌い
  • 人気指数は比較的高い
  • 最後にnode_を選択しましたacl、主に
  • の人気は比較的高く、約2000 star、
  • 次に機能自体が独立し、メモリ、mongo、redisの3つの記憶方式
  • を提供する.
  • API簡単で使いやすい
  • ドキュメント読みやすい
  • ソースコードは多くなく、
  • のカスタマイズと拡張が便利です.
  • 私達の主要なプログラムは開発し終わって、侵入が少なくて、研究した後にnodeを発見しますaclは
  • できるはずです
    node_の確認aclの後、いくつかの小さな細部と設計を研究し始め、こんなに多くのことを言って、自分でコードを書いてインタフェースの機能テストを行います.
    質問リスト:
  • 権限はaddRoleParentsを継承して需要を満たし、確かに使いやすい.例えばguest,user,adminの3つのRole,user集積guest,admin集積userである.次に、異なるロールに対してパーミッション構成を行います.悪くない
  • Resourceは同梱をサポートしていません.これはどういう意味ですか.例えば、メッセージには次のパスがあります./msg/delete、msg/add、/msg/list、犬の血Added the possibility to have a wildcard in resource name
  • すべてのRoleクエリーのAPIを提供していません.私が入った後、どのくらいのRole
  • があるか分かりません.
  • キャラクターを削除すると、
  • のデータが残ります.
  • は、ページまたはAPI
  • を関連付けるモジュールを導入する必要がある
  • 初期化には、スーパー管理者
  • が必要です.
  • デフォルト設定、すべて許可?すべてアクセスできませんか?
  • ディレクトリ継承関係を導入するかどうか
  • .
  • ......

  • ここはスリスリがこんなにたくさん書いていて、APIさえできれば、残りはデザインの問題です.面倒な問題が来た
  • Resourceは、プロビジョニング
  • をサポートしていません.
  • すべてのロールクエリを提供しないAPI
  • この2つの基本的な機能はサポートされておらず、卵も游んでいます.落ち着いて、落ち着いて、私たちはソースコードを開けて、プラグインは全部で7つのjs(バージョン0.4.11)
  • acl.jsコアのコアファイル、ロール、Resource、PermissionなどのAPI
  • の暴露
  • backend.js backend API定義、実用的な役割はありません
  • contract.jsパラメータ検証js
  • memory-backend.jsメモリに
  • を格納
  • mongodb-backend.js mongodbストレージ
  • redis-backend.js redisストレージ
  • index.jsデフォルトファイル
  • 3つのbackendはすべてデータを格納しているので、まずデータをエクスポートしてみましょう.redisのエクスポート方法について見てみましょう.
  • redis-dump
  • をインストール
  • edis-dump -h 127.0.0.1 -d 0 --json > c:\db.json

  • エクスポートされたファイルを見てみましょう
    {
        "acl_allows_/@guest": {
            "type": "set",
            "value": [
                "*"
            ]
        },
        "acl_allows_/about@guest": {
            "type": "set",
            "value": [
                "*"
            ]
        },
        "acl_allows_/index@guest": {
            "type": "set",
            "value": [
                "*"
            ]
        },
        "acl_meta@roles": {
            "type": "set",
            "value": [
                "guest"
            ]
        },
        "acl_meta@users": {
            "type": "set",
            "value": [
                "1024"
            ]
        },
        "acl_resources@guest": {
            "type": "set",
            "value": [
                "/",
                "/about",
                "/index"
            ]
        },
        "acl_roles@user": {
            "type": "set",
            "value": [
                "1024"
            ]
        },
        "acl_users@1024": {
            "type": "set",
            "value": [
                "user"
            ]
        }
    }
  • aclは接頭辞で、aclを初期化するときに設定できます
    var acl = require('acl');
    // Using redis backend
    acl = new acl(new acl.redisBackend(redisClient, 'acl'));

  • acl_meta@roles,acl_meta@users,acl_meta@usersacl_meta@rolesは格納されているすべてのRoleを表し、他の同理はコードaclにひっくり返す.jsはシステムがどのようにあるユーザーのRolesの
  • を取るかを見てみましょう.
     /**
      userRoles( userId, function(err, roles) )
    
      Return all the roles from a given user.
    
      @param {String|Number} User id.
      @param {Function} Callback called when finished.
      @return {Promise} Promise resolved with an array of user roles
    */
    Acl.prototype.userRoles = function(userId, cb){
      return this.backend.getAsync(this.options.buckets.users, userId).nodeify(cb);
    };

    this.options.buckets.usersは何者だ?
       options = _.extend({
        buckets: {
          meta: 'meta',
          parents: 'parents',
          permissions: 'permissions',
          resources: 'resources',
          roles: 'roles',
          users: 'users'
        }
      }, options);

    this.options.buckets.users:usersテキストです.では、これらのパラメータ「acl」、「users」、「1024」を連想します.驚いたかどうか見てみましょう.意外です.
        "acl_users@1024": {
            "type": "set",
            "value": [
                "user"
            ]
        }

    簡単だよjsにはbucketKeyという方法があります.専門ユーザーが格納しているkeyをつなぎ合わせるので、どんなデータを手に入れたいのか、考え方は簡単です.
      bucketKey : function(bucket, keys){
        var self = this;
        if(Array.isArray(keys)){
          return keys.map(function(key){
            return self.prefix+'_'+bucket+'@'+key;
          });
        }else{
          return self.prefix+'_'+bucket+'@'+keys;
        }
      }

    現在のすべてのロールを取得し、どのように取得するかは、主にスーパー管理者に与えられます.接合部aclだけでmeta@rolesを選択すると、すべてのキャラクタを取得できます.
    /**
      allRoles( userId)
    
           Role
    
      @param {String|Number}  Id 
    **/
    Acl.prototype.allRoles = function (userId) {
        contract(arguments)
            .params('string|number')
            .end()
        return userId ? this.userRoles(userId) :
            this.backend.getAsync(this.options.buckets.meta, this.options.buckets.roles)
                .then(roles => roles.filter(r => !!r))
    }

    これまで,データ構造を解析した後,多くのインタフェースが露出していないデータを得ることができた.私たちが最も関心を持っている問題に戻って、これは通マッチングをサポートしていません.どうすればいいですか??
  • Mongodb-backendプロジェクトは主にredisを考慮し、これは
  • を考慮しない.
  • プラグインがクエリーしましたnode_aclのプラグインはいくつかありますが、より多くのストレージをサポートする
  • のようです.
  • カスタム拡張
  • ディレクトリ権限継承これは
  • を考慮することができる.
  • 手動メンテナンス、aclに協力する.middlewareの最初のパラメータは、ディレクトリを限定するのは気まずい
  • です.
  • Acl.middleware+追加開発ミドルウェア()の修正が多く、感じが悪い
  • await next()の後、戻る前に再び邪魔な考えをブロックします
  • ここでは、カスタム拡張を考慮して、APIを静かに見てみましょう.
  • userId => roles (userRoles)
  • roles=>resources|実際の条件に基づいてキャッシュ(whatResources)
  • resourcesでpathをマッチングし、条件を満たすresources|resource
  • を検索する
  • 一致するresourceクエリーアクセス権(isAllowed)
  • 1,2,4はいずれも既存のAPIがあり,3だけが自分で実現しなければならないが,ここではpath-to-regexpについて言及し,expressとkoaはこれに基づいてルーティングマッチングを表示しているので,私は上記の考えを持っている.
    /**
      getMappedRerouces(path,resources)
    
                  
    
      @param {String|Number}        
      @param {Array}           Resource
    */
    function getMappedRerouces(path, resources) {
        return [].concat(resources.filter(r = dbRe => {
            //TODO::      option  
            let re = pathToRegexp(dbRe)
            return !!re.exec(path)
        }))
    }

    これは現在pathマッチングを要求しているすべてのResourceを取得することができ、複数である可能性が高いので、どのようにすれば、いずれのマッチングも可能であるはずです.最後のコードは
    const Acl = require('acl')
    const contract = require('../node_modules/[email protected]@acl/lib/contract')
    const pathToRegexp = require('path-to-regexp')
    
    const originalIsAllowed = Acl.prototype.isAllowed
    
    /**
      getMappedRerouces(path,resources)
    
                  
    
      @param {String|Number}        
      @param {Array}           Resource
    */
    function getMappedRerouces(path, resources) {
        return [].concat(resources.filter(r = dbRe => {
            //TODO::      option  
            let re = pathToRegexp(dbRe)
            return !!re.exec(path)
        }))
    }
    
    /**
      getAllResources( userId)
    
                  
    
      @param {String|Number}  Id 
    */
    Acl.prototype.allResources = function (userId) {
        contract(arguments)
            .params('string|number')
            .end()
        return userId ? this.userRoles(userId).then(roles => this.whatResources(roles)) : this._allResources()
    }
    
    Acl.prototype._allResources = function () {
        return this.allRoles()
            .then(roles => this.backend.unionAsync(this.options.buckets.resources, roles))
    }
    
    /**
      allRoles( userId)
    
           Role
    
      @param {String|Number}  Id 
    */
    Acl.prototype.allRoles = function (userId) {
        contract(arguments)
            .params('string|number')
            .end()
        return userId ? this.userRoles(userId) :
            this.backend.getAsync(this.options.buckets.meta, this.options.buckets.roles)
                .then(roles => roles.filter(r => !!r))
    }
    
    
    /**
      isAllowed( userId, resource, permissions, function(err, allowed) )
    
      Checks if the given user is allowed to access the resource for the given
      permissions (note: it must fulfill all the permissions).
    
      @param {String|Number} User id.
      @param {String|Array} resource(s) to ask permissions for.
      @param {String|Array} asked permissions.
      @param {Function} Callback called wish the result.
    */
    Acl.prototype.isAllowed = function (userId, resource, permissions, cb) {
        contract(arguments)
            .params('string|number', 'string', 'string|array', 'function')
            .params('string|number', 'string', 'string|array')
            .end();
    
        let args = [...arguments]
        // 1.userId => roles
        // 2.roles => resources |         
        // 3.  resources   path,        resources|resource
        // 4.     resource      
        return this.allResources(userId)
            .then(dbRe => getMappedRerouces(resource, Object.keys(dbRe)))
            .then(resources => {
                //   resource              
                return Promise.all((resources || []).map(re => {
                    return originalIsAllowed.apply(this, [args[0], re, ...args.slice(2)])
                }))
            }).then(allows => {
                return allows.some(Boolean)
            })
    }
    
    module.exports = Acl

    どのように使うか、
  • 権限設定
  • ミドルウェアブロック
  • 権限の設定
        acl.allow([        
            {
                roles: 'user',
                allows: [
                    {
                        resources: ['/msg', '/msg/:id', '/download', '/activities','/msg/(.*)'],
                        permissions: '*'
                    }
                ]
            }
      ])

    ミドルウェアブロック
    const acl = require('../acl')
    //const getAllRouter = require('./util/getAllRouter')
    const pathToRegexp = require('path-to-regexp')
    
    const loginPath = '/login'
    
    module.exports = app => {
        async function aclmd(req, res, next) {
            var userId = 1024
            if (userId) {
                const path = req.path
                if (path == loginPath) {
                    await next()
                } else {
                    //const aa = await anyMatch(path, userId, acl)
                    const allowed = await acl.isAllowed(userId, path, '*')
                    if (allowed) {                  
                        next()
                    } else {              
                        res.redirect(loginPath)
                        res.end();
                    }
                }
            } else {
                res.redirect(loginPath)
                res.end();
            }
        }
        app.use(aclmd)
    }

    Node acl demonode権限制御モジュールnode_aclの応用