shiro 自定義攔截器配置多個權限實現

一、問題產生

在項目開發過程中,安全對於後臺管理很重要。shiro是一個比較常流行的安全框架,網上關於shiro的使用和實現原理也比較全面。這裏不做詳細介紹,在項目中的權限配置會有各種不同的需求,例如有的url需要用戶擁有多個權限中的一個權限就能夠訪問,這個就要自己編寫攔截器的規則。

二、具體場景

自定義的攔截器沒有重寫PermissionsAuthorizationFilter的isAccessAllowed方法,使用的權限控制的默認的攔截器。yml文件的全蠍配置如下:

   - /user/getStatis-->e-perms[green,user]
   - /user/findUserByNameAndTime-->e-perms[air,user,wallet]
   - /greenplant/findTreesType-->e-perms[seeds,plants]
   - /wallet/findWalletByCondition-->e-perms[user,wallet]

例如:/user/getStatis的業務需求是擁有green或者user權限的用戶都要有權限能調用該接口。但是上面的配置導致無論是擁有green或者user權限的用戶都不能訪問該接口。

三、原因分析

shiro權限控制是在用戶登錄時會再realm中增加該用戶的權限信息,在登錄的時候會根據請求的url和相關的權限做映射。在用戶請求具體url時,會根據url獲得對應的權限,在到攔截器中做權限的校驗。上面配置不生效的原因是因爲shiro的下面代碼,該代碼是在PermissionsAuthorizationFilter中,默認的權限校驗規則。

public boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) throws IOException {
        Subject subject = this.getSubject(request, response);
        String[] perms = (String[])((String[])mappedValue);
        boolean isPermitted = true;
        if (perms != null && perms.length > 0) {
            if (perms.length == 1) {
                if (!subject.isPermitted(perms[0])) {
                    isPermitted = false;
                }
            } else if (!subject.isPermittedAll(perms)) {
                isPermitted = false;
            }
        }

        return isPermitted;
    }

從上面的代碼可以看出,我們的配置會默認被搶轉爲string類型的字符串數組。當只有一個權限時,會直接判斷有沒有該權限;當配置多個權限時,從下面的代碼可以看出只用在請求url的用戶擁有所有的權限時,纔會返回true,否則就會被拒絕訪問。

protected boolean isPermittedAll(Collection<Permission> permissions, AuthorizationInfo info) {
        if (permissions != null && !permissions.isEmpty()) {
            Iterator var3 = permissions.iterator();

            while(var3.hasNext()) {
                Permission p = (Permission)var3.next();
                if (!this.isPermitted(p, info)) {
                    return false;
                }
            }
        }

        return true;
    }

四、問題解決

shiro源碼只考慮了權限的和的問題沒有通過配置解決權限的或的問題。因此爲了滿足業務需求,在自定義攔截器中需要重寫PermissionsAuthorizationFilter的isAccessAllowed方法。爲了滿足“和”和“或”的不同權限配置的需求,對isAccessAllowed方法做如下修改:

@Override
    public boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) throws IOException {
        Subject subject = this.getSubject(request, response);
        String[] perms = (String[]) ((String[]) mappedValue);
        boolean isPermitted = true;
        if (perms != null && perms.length > 0) {
            if (perms.length == 1) {
                if (!isOneOfPermitted(perms[0], subject)) {
                    isPermitted = false;
                }
            } else if (!isAllPermitted(perms,subject)) {
                isPermitted = false;
            }
        }
        return isPermitted;
    }

    /**
     * 以“,”分割的權限爲並列關係的權限控制,分別對每個權限字符串進行“|”分割解析
     * 若並列關係的權限有一個不滿足則返回false
     *
     * @param permStrArray 以","分割的權限集合
     * @param subject      當前用戶的登錄信息
     * @return 是否擁有該權限
     */
    private boolean isAllPermitted(String[] permStrArray, Subject subject) {
        boolean isPermitted = true;
        for (int index = 0, len = permStrArray.length; index < len; index++) {
            if (!isOneOfPermitted(permStrArray[index], subject)) {
                isPermitted = false;
            }
        }
        return isPermitted;
    }

    /**
     * 判斷以“|”分割的權限有一個滿足的就返回true,表示權限的或者關係
     *
     * @param permStr 權限數組種中的一個字符串
     * @param subject 當前用戶信息
     * @return 是否有權限
     */
    private boolean isOneOfPermitted(String permStr, Subject subject) {
        boolean isPermitted = false;
        String[] permArr = permStr.split("\\|");
        if (permArr.length > 0) {
            for (int index = 0, len = permArr.length; index < len; index++) {
                if (subject.isPermitted(permArr[index])) {
                    isPermitted = true;
                }
            }
        }
        return isPermitted;
    }

方法做了上述修改,保持“,”表示多個權限的並列關係,每個“,”分割的字符串可能是多個“或”權限的集合,再用“|”分割字符串,“|”需要轉義。修改代碼後,權限配置做了如下修改:

   - /user/getStatis-->e-perms[green|user]
   - /user/findUserByNameAndTime-->e-perms[air|user|wallet]
   - /greenplant/findTreesType-->e-perms[seeds|plants]
   - /wallet/findWalletByCondition-->e-perms[user|wallet]

這樣就能是使得擁有green或者user權限的用戶都能成功的訪問 /user/getStatis接口了,同時兼容了shiro自帶的權限檢驗規則。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章