zk源碼閱讀9:數(shù)據(jù)模型ACL之基礎數(shù)據(jù)結構以及驗證

摘要

DataTree涉及到ACL浪册,本節(jié)先講解ACL相關內容
講ACL的參考資料并不是很多,書上也沒有講原理實現(xiàn)岗照,這里自己整理一下
本文主要講解

ACL簡介
ACL數(shù)據(jù)結構
  perms
  Id(id,schema)
  內置權限村象,ACL列表,Id攒至,schema
ACL的創(chuàng)建厚者,修改
ACL的驗證
  ACL創(chuàng)建修改的驗證(create,setACL)
  ACL申請權限的驗證(各種操作)

簡介

ZooKeeper使用ACL來控制訪問其znode(ZooKeeper的數(shù)據(jù)樹的數(shù)據(jù)節(jié)點)。ACL的實現(xiàn)方式非常類似于UNIX文件的訪問權限:它采用訪問權限位 允許/禁止 對節(jié)點的各種操作以及能進行操作的范圍迫吐。不同于UNIX權限的是库菲,ZooKeeper的節(jié)點不局限于 用戶(文件的擁有者),組和其他人(其它)這三個標準范圍志膀。ZooKeeper不具有znode的擁有者的概念熙宇。相反,ACL指定id集以及與之對應的權限溉浙。

還要注意的是一條ACL僅針對于一個特定的節(jié)點烫止。尤其不適用于子節(jié)點。
例如戳稽,如果/app 只對IP:172.16.16.1可讀 而 / APP/status 是對任何人可讀的馆蠕,ACL不是遞歸的。

ZooKeeper支持可插拔的身份驗證方案惊奇。 id使用如下形式 scheme:id荆几,其中 scheme 是 id 所對應一個認證方案。例如赊时,IP:172.16.16.1吨铸,id為主機的地址 172.16.16.1。

當客戶端連接到ZooKeeper驗證自己時祖秒,ZooKeeper將有關該客戶端的所有Id與客戶連接關聯(lián)诞吱。客戶端試圖訪問一個節(jié)點時竭缝,這些ID與該znodes的ACL驗證房维。 ACL是由(scheme:expression, perms)對構成。其中expression的格式指定為scheme抬纸。例如咙俩,(IP:19.22.0.0/16,READ)表示對所有起始IP為19.22的客戶端具有讀權限。

一個ZooKeeper的節(jié)點(znode)存儲兩部分內容:數(shù)據(jù)和狀態(tài)阿趁,狀態(tài)中包含ACL信息膜蛔。創(chuàng)建一個znode會產生一個ACL列表。

那么脖阵,ACL具體是什么呢皂股,怎么實現(xiàn)的?

ACL數(shù)據(jù)結構

代碼里面涉及ACL機制的類有

org.apache.zookeeper.data.ACL
  包含權限perms與Id(見下)

org.apache.zookeeper.data.Id
  包含驗證模式schema和提供的驗證內容id
  
org.apache.zookeeper.ZooDefs
  提供內置的OpCode
  權限Perms
  ACL列表定義Ids

先用一張圖說明ACL與Id這兩個類的依賴關系


ACL與Id依賴關系

也可以說,每個ACL包括:

  驗證模式(scheme)
  具體內容(Id)(當scheme=“digest”時,Id為用戶名密碼命黔,例如“root:J0sTy9BCUKubtK1y8pkbL7qoxSw=”)
  權限(perms)

下面分開進行介紹這兩個結構(perms,Id),也可以說是三個結構(perms,id,schema),這里根據(jù)類的定義來呜呐,還是當成兩個數(shù)據(jù)結構來講,ACL數(shù)據(jù)結構如下

public class ACL implements Record {
  private int perms;
  private Id id;
}

權限perms

目前,節(jié)點的權限(perms)有以下幾種悍募,在org.apache.zookeeper.ZooDefs.Perms 中定義

        int READ = 1 << 0;//允許對本節(jié)點GetChildren和GetData操作
        int WRITE = 1 << 1;//允許對本節(jié)點SetData操作
        int CREATE = 1 << 2;//允許對子節(jié)點Create操作
        int DELETE = 1 << 3;//允許對子節(jié)點Delete操作
        int ADMIN = 1 << 4;//允許對本節(jié)點setAcl操作
        int ALL = READ | WRITE | CREATE | DELETE | ADMIN;//這個是組合權限

ACL權限用一個int型數(shù)字perms表示
perms的5個二進制位分別表示setacl蘑辑、delete、create坠宴、write洋魂、read
比如0x1f=adcwr,0x1=----r啄踊,0x15=a-c-r忧设。
除了ALL以外刁标,其他都是最細粒度的權限颠通,可以用|,&來自己定義perms的組合權限

Id

包含驗證模式schema以及提供驗證的內容id
目前zk提供了兩個內置的Id,在org.apache.zookeeper.ZooDefs.Ids中定義

/**
         * This Id represents anyone.
         */
        public final Id ANYONE_ID_UNSAFE = new Id("world", "anyone");//固定用戶為anyone膀懈,為所有Client端開放權限

        /**
         * This Id is only usable to set ACLs. It will get substituted with the
         * Id's the client authenticated with.
         */
        public final Id AUTH_IDS = new Id("auth", "");//不使用任何id顿锰,代表任何已確認用戶。

以及在org.apache.zookeeper.server.auth.DigestAuthenticationProvider#handleAuthentication 定義的

new Id("super", "")//在這種scheme情況下启搂,對應的id擁有超級權限硼控,可以做任何事情(cdrwa)

除了內置Id以外,還有內置的schema提供認證模式胳赌,但是沒有對應的默認id(因為都是動態(tài)提供的)

schema

除了上面內置Id定義的world和auth,super這三個schema牢撼,還有無固定id的內置的schema即驗證模式
驗證模式以及驗證方法通過AuthenticationProvider實現(xiàn)

AuthenticationProvider的三種實現(xiàn)

最終在ProviderRegistry中進行注冊

digest:Client端由用戶名和密碼驗證,譬如user:password疑苫,digest的密碼生成方式是Sha1摘要的base64形式
ip:Client端由IP地址驗證熏版,譬如172.2.0.0/24
Sasl: 這個類定義了,但是并沒有注冊,我也并不清楚這個認證方式

下面對Id做一個總結

schema id 意義 備注
auth "" 不使用任何id捍掺,代表任何已確認用戶 自帶Id
world anyone 固定用戶撼短,為所有Client端開放權限 自帶Id
super "" 擁有超級權限,可以做任何事情(cdrwa) 自帶Id
ip 無固定值挺勿,有固定格式(ip expression) IP驗證方式 自帶schema
digest 無固定值曲横,有固定格式(digest expression) 用戶名和密碼驗證,再生成摘要 自帶schema
sasl 無固定值不瓶,有固定格式(sasl expression) sasl驗證方式禾嫉,這個我并不是很懂 自帶schema

ACL的創(chuàng)建與修改

只有兩類API會改變Znode的ACL列表:一個是create()灾杰,一個是setACL()。
這兩個方法都要求傳入一個List夭织。Server接到這兩種更新請求后吭露,會判斷指定的每一個ACL中,scheme對應的AuthenticationProvider是否存在尊惰。
如果存在讲竿,調用其isValid(String)方法判斷對應的id表達式是否合法
具體參見PrepRequestProcessor.fixupACL()方法。

ACL的驗證

ACL創(chuàng)建修改時的驗證

只在create和setACL操作中涉及ACL的創(chuàng)建與修改弄屡,具體參見PrepRequestProcessor.fixupACL()

private boolean fixupACL(List<Id> authInfo, List<ACL> acl) {
        if (skipACL) {
            return true;
        }
        if (acl == null || acl.size() == 0) {
            return false;
        }

        Iterator<ACL> it = acl.iterator();
        LinkedList<ACL> toAdd = null;
        while (it.hasNext()) {
            ACL a = it.next();
            Id id = a.getId();
            if (id.getScheme().equals("world") && id.getId().equals("anyone")) {//如果是固定用戶题禀,為所有Client端開放權限
                // wide open
            } else if (id.getScheme().equals("auth")) {
                // This is the "auth" id, so we have to expand it to the
                // authenticated ids of the requestor
                it.remove();//如果是auth,把這個從acl的List中刪掉
                if (toAdd == null) {
                    toAdd = new LinkedList<ACL>();
                }
                boolean authIdValid = false;
                for (Id cid : authInfo) {
                    /*
                    一般情況下膀捷,默認的Id只有IP這一種(org.apache.zookeeper.server.NIOServerCnxn.NIOServerCnxn)迈嘹,里面調用了
                    authInfo.add(new Id("ip", addr.getHostAddress()));
                     */
                    AuthenticationProvider ap =
                        ProviderRegistry.getProvider(cid.getScheme());
                    if (ap == null) {
                        LOG.error("Missing AuthenticationProvider for "
                                + cid.getScheme());
                    } else if (ap.isAuthenticated()) {//如果驗證過了,三種實現(xiàn)中,IP返回false全庸,其他兩種返回true
                        authIdValid = true;
                        toAdd.add(new ACL(a.getPerms(), cid));
                    }
                }
                if (!authIdValid) {
                    return false;
                }
            } else {//其他認證模式的話,如ip秀仲,digest,sasl
                AuthenticationProvider ap = ProviderRegistry.getProvider(id
                        .getScheme());
                if (ap == null) {
                    return false;
                }
                if (!ap.isValid(id.getId())) {//如果id的格式不valid
                    return false;
                }
            }
        }
        if (toAdd != null) {
            for (ACL a : toAdd) {
                acl.add(a);
            }
        }
        return acl.size() > 0;//確保有一種方式認證通過了
    }

簡而言之壶笼,這個函數(shù)就是看設置的ACL值是否合理,基本過程如下

1.如果acl列表有("world","anyone"),那么一定認證通過
2.上述情況外神僵,如果是Id的schema是"auth",那么要看請求攜帶的authInfo是否是isAuthenticated的,是的話認證通過
3.上述情況外,一般就是“ip”,"digest","sasl"覆劈,調用對應認證提供器的isValid方法校驗id內容格式是否valid,是的話認證通過

實例分析

下面分析一個用"auth",""這個Id創(chuàng)建節(jié)點出現(xiàn)的異常

背景

String path1 = zk.create("/test21", "asd".getBytes(),
                    ZooDefs.Ids.CREATOR_ALL_ACL,
                    CreateMode.EPHEMERAL);

其中CREATOR_ALL_ACL定義在org.apache.zookeeper.ZooDefs.Ids#CREATOR_ALL_ACL中

public final ArrayList<ACL> CREATOR_ALL_ACL = new ArrayList<ACL>(
                Collections.singletonList(new ACL(Perms.ALL, AUTH_IDS)));//用到了"auth",""

出現(xiàn)了異常

org.apache.zookeeper.KeeperException$InvalidACLException: KeeperErrorCode = InvalidACL for /test21

原理分析

NIOServerCnxn#NIOServerCnxn只給request的authInfo加了"ip"這個schema
PrepRequestProcessor#fixupACL中保礼,處理邏輯如下

異常分析

也就是說("auth","")應該和sasl或者digest這種schema配合起來使用才行,這里就深究如何使用"auth",""了

申請權限時的驗證(以create為例)

應該會在后面處理鏈的時候講责语,這里帶過一下
比如在parentNode中進行createNode操作炮障,參見
org.apache.zookeeper.server.PrepRequestProcessor#pRequest2Txn 中 case OpCode.create
調用了

checkACL(zks, parentRecord.acl, ZooDefs.Perms.CREATE,
                        request.authInfo);//驗證是否有create權限

checkACL函數(shù)如下

/**
     *
     * @param zks
     * @param acl 對應節(jié)點或者父節(jié)點擁有的權限
     * @param perm 目前操作需要的權限
     * @param ids 目前請求提供的權限
     * @throws KeeperException.NoAuthException
     */
    static void checkACL(ZooKeeperServer zks, List<ACL> acl, int perm,
            List<Id> ids) throws KeeperException.NoAuthException {
        if (skipACL) {//如果跳過ACL
            return;
        }
        if (acl == null || acl.size() == 0) {//如果沒有要求的ACL
            return;
        }
        for (Id authId : ids) {
            if (authId.getScheme().equals("super")) {//如果提供的ACL有超級權限
                return;
            }
        }
        for (ACL a : acl) {
            Id id = a.getId();
            if ((a.getPerms() & perm) != 0) {//如果對應的節(jié)點擁有perm權限
                if (id.getScheme().equals("world")
                        && id.getId().equals("anyone")) {
                    return;//如果請求提供了超級權限
                }
                AuthenticationProvider ap = ProviderRegistry.getProvider(id
                        .getScheme());//根據(jù)策略模式獲取對應的認證提供器
                if (ap != null) {
                    for (Id authId : ids) {//用認證器一個個 認證 請求提供的Id
                        if (authId.getScheme().equals(id.getScheme())
                                && ap.matches(authId.getId(), id.getId())) {//模式相同并且匹配通過
                            //這要有一個匹配通過就行
                            return;
                        }
                    }
                }
            }
            //如果對應的節(jié)點都沒有要求的perm權限,那就驗證失敗坤候,和請求提供什么權限無關
        }
        throw new KeeperException.NoAuthException();
    }

簡而言之胁赢,就是當前節(jié)點acl包含當前操作權限perm,并且當前節(jié)點acl能夠認證通過請求提供的ids權限(有一個認證通過就行)

思考

注意上面描述的Id與id的區(qū)別

在Id這個類中,有id這個屬性白筹,注意大小寫

ACL與Id的關系

ACL包含perms與Id
Id包含Id和schema

("super","")和("world","anyone")權限比較

從上面的checkACL函數(shù)來講智末,先遇到("super","")就return
實際上,("super","")并沒有分配權限遍蟋,就像是程序開的后門吹害,遇到了這個Id,就通行虚青。

而("world","anyone")它呀,還進行了perm的分配,有對應的ACL,參照ZooDefs.Ids#ANYONE_ID_UNSAFE
在上面checkACL函數(shù)中,該Id還受限于

if ((a.getPerms() & perm) != 0)

也就是說纵穿,原來的節(jié)點權限不包含當前需要的perm權限時下隧,("world","anyone")也沒用
所以結論就是*** ("super","")權限更大***

權限的創(chuàng)建修改,以及申請權限的驗證

在對節(jié)點進行create和setACL時涉及權限的創(chuàng)建和修改谓媒,主要驗證acl列表的合理性
在org.apache.zookeeper.server.PrepRequestProcessor#fixupACL判斷

在對節(jié)點進行操作時淆院,需要驗證當前請求以及相關節(jié)點是否有對應的權限
在org.apache.zookeeper.server.PrepRequestProcessor#checkACL判斷

問題

創(chuàng)建最后將ACL信息保存在znode狀態(tài)中,這是怎么實現(xiàn)的?
這個在后面請求鏈中再看

(auth,"")這個Id到底該怎么配合digest或者sasl使用句惯,沒有深究

吐槽

Id對應常量的地方有點亂土辩,比如super定義在DigestAuthenticationProvider中

AuthenticationProvider 三個實現(xiàn)類的#isValid都沒有注釋
要自己看才知道對應schema該寫的id(即驗證內容)的格式應該是怎么樣的

(auth,"")這個Id,使用說明太少了,沒看到demo也沒見到合理的資料

refer

主要參照 http://www.cnblogs.com/linuxbug/p/5023677.html
認證提供與注冊的相關介紹
一些定義
http://aliapp.blog.51cto.com/8192229/1327674
《從paxos到zookeeper》第7章

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末抢野,一起剝皮案震驚了整個濱河市拷淘,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌指孤,老刑警劉巖启涯,帶你破解...
    沈念sama閱讀 219,366評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異恃轩,居然都是意外死亡结洼,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,521評論 3 395
  • 文/潘曉璐 我一進店門叉跛,熙熙樓的掌柜王于貴愁眉苦臉地迎上來松忍,“玉大人,你說我怎么就攤上這事昧互⊥焯” “怎么了伟桅?”我有些...
    開封第一講書人閱讀 165,689評論 0 356
  • 文/不壞的土叔 我叫張陵敞掘,是天一觀的道長。 經常有香客問我楣铁,道長玖雁,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,925評論 1 295
  • 正文 為了忘掉前任盖腕,我火速辦了婚禮赫冬,結果婚禮上,老公的妹妹穿的比我還像新娘溃列。我一直安慰自己劲厌,他們只是感情好,可當我...
    茶點故事閱讀 67,942評論 6 392
  • 文/花漫 我一把揭開白布听隐。 她就那樣靜靜地躺著补鼻,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上风范,一...
    開封第一講書人閱讀 51,727評論 1 305
  • 那天咨跌,我揣著相機與錄音,去河邊找鬼硼婿。 笑死锌半,一個胖子當著我的面吹牛,可吹牛的內容都是我干的寇漫。 我是一名探鬼主播刊殉,決...
    沈念sama閱讀 40,447評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼州胳!你這毒婦竟也來了冗澈?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 39,349評論 0 276
  • 序言:老撾萬榮一對情侶失蹤陋葡,失蹤者是張志新(化名)和其女友劉穎亚亲,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體腐缤,經...
    沈念sama閱讀 45,820評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡捌归,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,990評論 3 337
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了岭粤。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片惜索。...
    茶點故事閱讀 40,127評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖剃浇,靈堂內的尸體忽然破棺而出巾兆,到底是詐尸還是另有隱情,我是刑警寧澤虎囚,帶...
    沈念sama閱讀 35,812評論 5 346
  • 正文 年R本政府宣布角塑,位于F島的核電站,受9級特大地震影響淘讥,放射性物質發(fā)生泄漏圃伶。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,471評論 3 331
  • 文/蒙蒙 一蒲列、第九天 我趴在偏房一處隱蔽的房頂上張望窒朋。 院中可真熱鬧,春花似錦蝗岖、人聲如沸侥猩。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,017評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽欺劳。三九已至洛退,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間杰标,已是汗流浹背兵怯。 一陣腳步聲響...
    開封第一講書人閱讀 33,142評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留腔剂,地道東北人媒区。 一個月前我還...
    沈念sama閱讀 48,388評論 3 373
  • 正文 我出身青樓,卻偏偏與公主長得像掸犬,于是被迫代替她去往敵國和親袜漩。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,066評論 2 355

推薦閱讀更多精彩內容

  • 一湾碎、ZooKeeper的背景 1.1 認識ZooKeeper ZooKeeper---譯名為“動物園管理員”宙攻。動物...
    algernoon閱讀 9,071評論 1 106
  • Spring Cloud為開發(fā)人員提供了快速構建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務發(fā)現(xiàn)介褥,斷路器座掘,智...
    卡卡羅2017閱讀 134,668評論 18 139
  • ZooKeeper是一個分布式的,開放源碼的分布式應用程序協(xié)調服務柔滔,它包含一個簡單的原語集溢陪,分布式應用程序可以基于...
    Prize閱讀 249評論 0 1
  • 你, 不再是我女朋友睛廊, 我形真, 也不再是你男朋友, 愛超全, 卻扯著我的心咆霜, 恨, 卻在愛中找到一絲; 那傻傻的等待嘶朱, ...
    林上的雪閱讀 276評論 0 1
  • “世之人以為養(yǎng)形足以存生蛾坯,而養(yǎng)形果不足以存生,則世奚足為哉见咒!”世人都有一種迷信的思想偿衰,認為“養(yǎng)形足以存生”挂疆。找到好...
    庶矣齋閱讀 641評論 0 6