目錄
基於shiro的自訂註解的擴展
如何透過邏輯進行頁面與api介面的關聯
shiro的自身註解的用法
如何寫自訂註解
首頁 Java java教程 基於shiro的自訂註解的擴展-圖文詳解

基於shiro的自訂註解的擴展-圖文詳解

Aug 09, 2018 pm 05:19 PM
java spring

基於shiro的自訂註解的擴展

這裡我們主要採取了shiro的自訂註解的方案。本篇文章主要解決以下的問題。

  1. 如何透過邏輯進行頁面與api介面的關聯。

  2. shiro的自身註解的用法。

  3. 如何寫自訂註解。

如何透過邏輯進行頁面與api介面的關聯

在表格與表格的結構關係中,頁面和介面表最終都是與權限表進行的關聯(詳情請查看我的上一篇文章《權限設計的雜談》)。
基於shiro的自訂註解的擴展-圖文詳解我們現在希望用另一個方案去取代他,實現一個低成本同時兼顧一定程度的權限控制。這裡我們引入兩個概念。 業務模組作業類型

  • 業務模組

    • #概念:將系統中的業務模組抽象化成一種數據,我們可以用字串的形式去表示,例如:角色管理對應是role-manage、使用者管理對應是user-manage等等。我們將系統中所存在的業務模組透過「最小特權原則」來劃分,最終形成一批可分配的資料。

    • 使用原則:api介面與頁面以及功能本質上來說,都和業務模組有邏輯關係,於是,我們可以對api介面與頁面(以及功能點)進行邏輯匹配,來判斷頁面與介面的關係。

  • 操作類型

    • #概念:將系統中的所有的運算類型抽象化成一種數據,我們也可以用字串的形式去表示,例如:新增對應的是add、分配對應的是allot等等。我們將系統中所有的操作類型根據業務模組透過「資料許可證」進行劃分,最終形成一批可分配的資料。

    • 使用原則:頁面是展示,功能點是動作,而介面是最終動作的資源提供,透過「業務模組」確定了調取的資源,透過「操作類型」確定了資源的使用方式。透過兩者可以大致無誤的判斷頁面的功能點觸發的介面是否在鑑權之內。

現在提出了這兩個概念,他們最終的實際的使用方式是什麼,我們先從以下幾個角度去思考一下。

  1. 資料庫中的頁表或的api介面表中的資料就是真實有效嗎?

  2. 頁面或介面的實際使用,是以功能存在為前提,還是以資料庫表中的資料存在為前提。

  3. 權限結構中,「控制物件」的儲存只有資料庫這一途徑嗎?

我們從結論出發來看這幾個問題,首先「控制物件」的儲存除了在資料庫中也可以程式碼中,也可以在設定檔中,不一定非得在資料庫;那麼接著回答第二個問題,當資料庫存在的介面訊息,而服務端並沒有開發這個介面的時候,資料庫的信本身就有問題,亦或者,資料庫裡新增的介面必定是服務端上已經部署的介面才能生效;接著就是第一個問題,那麼資料庫中關於「控制物件」的表中的資料並不一定是真實有效的。所以我們可以得到以下的解決方案

  1. 我們可以在介面上用註解的形式補充「業務模組」和「操作類型」的資料訊息,這兩類資訊都可以存於常數類別中,

  2. 在資料庫中新增建立頁面表結構和頁面功能表結構的時候,新增「業務模組」和「操作類型」字段。

  3. 可以將「業務模組」和「操作類型」的資訊存於資料庫的字典表中。

  4. 模組的新增或操作的新增,必定帶來了介面的新增,那麼就會帶來一次系統部署活動,這個運維成本是無法減少的,並不能透過表結構來減少。

基於shiro的自訂註解的擴展-圖文詳解

但是這種方案僅適用於非強控制介面型的項目,在強控制型的介面項目仍然要將頁面與介面進行綁定,雖然這會帶來巨大的運維成本。另外也可以透過介面路由規則進行劃分,例如:/api/page/xxxx/(僅對頁面使用),/api/mobile/xxxxx(僅對行動端使用)將僅供頁面使用的介面進行分類,這類別介面只做認證不做授權,也可以達到目的。

shiro的自身註解的用法

透過一個理論上的思路認可之後,剩下的則是付諸技術上的實踐,我們這邊採用的是Apache Shiro的安全框架,在Spring Boot的環境下應用。簡要說明以下幾個shiro的註解。

註解名稱
#@RequiresAuthentication #作用於的類別、方法、實例上。呼叫時,目前的subject是必須經過了認證的。
@RequiresGuest 作用於的類別、方法、實例上。呼叫時,subject可以是guest狀態。
@RequiresPermissions 作用於的類別、方法、實例上。呼叫時,需要判斷suject中是否包含目前介面中的Permission(權限資訊)。
@RequiresRoles 在作用的類別、方法、實例上。呼叫時,需要判斷subject中是否包含目前介面中的Role(角色資訊)。
@RequiresUser 作用於的類別、方法、實例上。呼叫時,需要判斷subject中是否目前應用中的使用者。
    /**
     * 1.当前接口需要经过"认证"过程
     * @return
     */
    @RequestMapping(value = "/info",method = RequestMethod.GET)
    @RequiresAuthentication
    public String test(){
        return "恭喜你,拿到了参数信息";
    }
    
    /**
     * 2.1.当前接口需要经过权限校验(需包含 角色的查询 或 菜单的查询)
     * @return
     */
    @RequestMapping(value = "/info",method = RequestMethod.GET)
    @RequiresPermissions(value={"role:search","menu":"search"},logical=Logical.OR)
    public String test(){
        return "恭喜你,拿到了参数信息";
    }
    
    /**
     * 2.2.当前接口需要经过权限校验(需包含 角色的查询 与 菜单的查询)
     * @return
     */
    @RequestMapping(value = "/info",method = RequestMethod.GET)
    @RequiresPermissions(value={"role:search","menu":"search"},logical=Logical.OR)
    public String test(){
        return "恭喜你,拿到了参数信息";
    }
    
    /**
     * 3.1.当前接口需要经过角色校验(需包含admin的角色)
     * @return
     */
    @RequestMapping(value = "/info",method = RequestMethod.GET)
    @RequiresRoles(value={"admin"})
    public String test(){
        return "恭喜你,拿到了参数信息";
    }
    
    /**
     * 3.2.当前接口需要经过角色与权限的校验(需包含admin的角色,以及角色的查询 或 菜单的查询)
     * @return
     */
    @RequestMapping(value = "/info",method = RequestMethod.GET)
    @RequiresRoles(value={"admin"})
    @RequiresPermissions(value={"role:search","menu":"search"},logical=Logical.OR)
    public String test(){
        return "恭喜你,拿到了参数信息";
    }
登入後複製

在我們的實際使用過程中,實際上只需要使用@RequiresPermissions和@RequiresAuthentication就可以了這一個註解就可以了,在上一小節的結尾,我們採取了業務模組與操作的結合方案來解耦頁面和api介面的關係,和apache Shiro的這種方式正好一致。但是@RequiresRoles這個我們盡可能不採用,因為角色的組合形式太多,角色名沒有辦法在接口中具象唯一化(很難指定接口歸某個角色調用,但是一定能知道接口歸屬於某些業務模組的某些操作。)

現在我們來回顧整個運作的流程。

基於shiro的自訂註解的擴展-圖文詳解

如何寫自訂註解

但是僅僅是擁有shiro中的這5個註解肯定是不夠使用的。在實際的使用過程中,根據需求,我們會在權限認證中加入我們自己特有的業務邏輯的,我們為了便捷則可以採用自訂註解的方式進行使用。這個方法不只適用於Apache Shiro,許多其他的框架如:Hibernate Validator、SpringMVC、甚至我們可以寫一套校驗體系,在aop中去驗證權限,這都是沒問題的。所以自訂註解的作用很廣。但是在這裡,我僅僅基於shiro的來實現適用於它的自訂註解。

  • 定義註解類別

/**
 * 用于认证的接口的注解,组合形式默认是“或”的关系
 */
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Auth {
    /**
     * 业务模块
     * @return
     */
    String[] module();
    /**
     * 操作类型
     */
    String[] action();

}
登入後複製
  • #定義註解的處理類別

/**
 * Auth注解的操作类
 */
public class AuthHandler extends AuthorizingAnnotationHandler {


    public AuthHandler() {
        //写入注解
        super(Auth.class);
    }

    @Override
    public void assertAuthorized(Annotation a) throws AuthorizationException {
        if (a instanceof Auth) {
            Auth annotation = (Auth) a;
            String[] module = annotation.module();
            String[] action = annotation.action();
            //1.获取当前主题
            Subject subject = this.getSubject();
            //2.验证是否包含当前接口的权限有一个通过则通过
            boolean hasAtLeastOnePermission = false;
            for(String m:module){
                for(String ac:action){
                    //使用hutool的字符串工具类
                    String permission = StrFormatter.format("{}:{}",m,ac);
                    if(subject.isPermitted(permission)){
                        hasAtLeastOnePermission=true;
                        break;
                    }
                }
            }
            if(!hasAtLeastOnePermission){
                throw new AuthorizationException("没有访问此接口的权限");
            }

        }
    }
}
登入後複製
  • 定義shiro攔截處理類別

/**
 * 拦截器
 */
public class AuthMethodInterceptor extends AuthorizingAnnotationMethodInterceptor {


    public AuthMethodInterceptor() {
        super(new AuthHandler());
    }

    public AuthMethodInterceptor(AnnotationResolver resolver) {
        super(new AuthHandler(), resolver);
    }

    @Override
    public void assertAuthorized(MethodInvocation mi) throws AuthorizationException {
        // 验证权限
        try {
            ((AuthHandler) this.getHandler()).assertAuthorized(getAnnotation(mi));
        } catch (AuthorizationException ae) {
            if (ae.getCause() == null) {
                ae.initCause(new AuthorizationException("当前的方法没有通过鉴权: " + mi.getMethod()));
            }
            throw ae;
        }
    }
}
登入後複製
  • #定義shiro的aop切面類別

/**
 * shiro的aop切面
 */
public class AuthAopInterceptor extends AopAllianceAnnotationsAuthorizingMethodInterceptor {
    public AuthAopInterceptor() {
        super();
        // 添加自定义的注解拦截器
        this.methodInterceptors.add(new AuthMethodInterceptor(new SpringAnnotationResolver()));
    }
}
登入後複製
  • 定義shiro的自訂註解啟動類別

/**
 * 启动自定义注解
 */
public class ShiroAdvisor extends AuthorizationAttributeSourceAdvisor {

    public ShiroAdvisor() {
        // 这里可以添加多个
        setAdvice(new AuthAopInterceptor());
    }

    @SuppressWarnings({"unchecked"})
    @Override
    public boolean matches(Method method, Class targetClass) {
        Method m = method;
        if (targetClass != null) {
            try {
                m = targetClass.getMethod(m.getName(), m.getParameterTypes());
                return this.isFrameAnnotation(m);
            } catch (NoSuchMethodException ignored) {

            }
        }
        return super.matches(method, targetClass);
    }

    private boolean isFrameAnnotation(Method method) {
        return null != AnnotationUtils.findAnnotation(method, Auth.class);
    }
}
登入後複製
總體的想法順序:定義註解類別(定義業務可使用的變數) ->定義註解處理類別(透過註解中的變數做業務邏輯處理)->定義註解的攔截器->定義aop的切面類別->最後定義shiro的自訂註解啟用類別。其他的自訂的註解的編寫思路和這個也是類似的。

相關推薦:

自訂註解映射thinkPHP21自訂標籤庫的導入方法詳解

Java中關於自訂註解的具體介紹

shiro授權實作詳解

以上是基於shiro的自訂註解的擴展-圖文詳解的詳細內容。更多資訊請關注PHP中文網其他相關文章!

本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

Video Face Swap

Video Face Swap

使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱工具

記事本++7.3.1

記事本++7.3.1

好用且免費的程式碼編輯器

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

禪工作室 13.0.1

禪工作室 13.0.1

強大的PHP整合開發環境

Dreamweaver CS6

Dreamweaver CS6

視覺化網頁開發工具

SublimeText3 Mac版

SublimeText3 Mac版

神級程式碼編輯軟體(SublimeText3)

熱門話題

Java教學
1664
14
CakePHP 教程
1422
52
Laravel 教程
1316
25
PHP教程
1267
29
C# 教程
1239
24
突破或從Java 8流返回? 突破或從Java 8流返回? Feb 07, 2025 pm 12:09 PM

Java 8引入了Stream API,提供了一種強大且表達力豐富的處理數據集合的方式。然而,使用Stream時,一個常見問題是:如何從forEach操作中中斷或返回? 傳統循環允許提前中斷或返回,但Stream的forEach方法並不直接支持這種方式。本文將解釋原因,並探討在Stream處理系統中實現提前終止的替代方法。 延伸閱讀: Java Stream API改進 理解Stream forEach forEach方法是一個終端操作,它對Stream中的每個元素執行一個操作。它的設計意圖是處

PHP:網絡開發的關鍵語言 PHP:網絡開發的關鍵語言 Apr 13, 2025 am 12:08 AM

PHP是一種廣泛應用於服務器端的腳本語言,特別適合web開發。 1.PHP可以嵌入HTML,處理HTTP請求和響應,支持多種數據庫。 2.PHP用於生成動態網頁內容,處理表單數據,訪問數據庫等,具有強大的社區支持和開源資源。 3.PHP是解釋型語言,執行過程包括詞法分析、語法分析、編譯和執行。 4.PHP可以與MySQL結合用於用戶註冊系統等高級應用。 5.調試PHP時,可使用error_reporting()和var_dump()等函數。 6.優化PHP代碼可通過緩存機制、優化數據庫查詢和使用內置函數。 7

PHP與Python:了解差異 PHP與Python:了解差異 Apr 11, 2025 am 12:15 AM

PHP和Python各有優勢,選擇應基於項目需求。 1.PHP適合web開發,語法簡單,執行效率高。 2.Python適用於數據科學和機器學習,語法簡潔,庫豐富。

PHP與其他語言:比較 PHP與其他語言:比較 Apr 13, 2025 am 12:19 AM

PHP適合web開發,特別是在快速開發和處理動態內容方面表現出色,但不擅長數據科學和企業級應用。與Python相比,PHP在web開發中更具優勢,但在數據科學領域不如Python;與Java相比,PHP在企業級應用中表現較差,但在web開發中更靈活;與JavaScript相比,PHP在後端開發中更簡潔,但在前端開發中不如JavaScript。

PHP與Python:核心功能 PHP與Python:核心功能 Apr 13, 2025 am 12:16 AM

PHP和Python各有優勢,適合不同場景。 1.PHP適用於web開發,提供內置web服務器和豐富函數庫。 2.Python適合數據科學和機器學習,語法簡潔且有強大標準庫。選擇時應根據項目需求決定。

PHP的影響:網絡開發及以後 PHP的影響:網絡開發及以後 Apr 18, 2025 am 12:10 AM

PHPhassignificantlyimpactedwebdevelopmentandextendsbeyondit.1)ItpowersmajorplatformslikeWordPressandexcelsindatabaseinteractions.2)PHP'sadaptabilityallowsittoscaleforlargeapplicationsusingframeworkslikeLaravel.3)Beyondweb,PHPisusedincommand-linescrip

Java程序查找膠囊的體積 Java程序查找膠囊的體積 Feb 07, 2025 am 11:37 AM

膠囊是一種三維幾何圖形,由一個圓柱體和兩端各一個半球體組成。膠囊的體積可以通過將圓柱體的體積和兩端半球體的體積相加來計算。本教程將討論如何使用不同的方法在Java中計算給定膠囊的體積。 膠囊體積公式 膠囊體積的公式如下: 膠囊體積 = 圓柱體體積 兩個半球體體積 其中, r: 半球體的半徑。 h: 圓柱體的高度(不包括半球體)。 例子 1 輸入 半徑 = 5 單位 高度 = 10 單位 輸出 體積 = 1570.8 立方單位 解釋 使用公式計算體積: 體積 = π × r2 × h (4

PHP:許多網站的基礎 PHP:許多網站的基礎 Apr 13, 2025 am 12:07 AM

PHP成為許多網站首選技術棧的原因包括其易用性、強大社區支持和廣泛應用。 1)易於學習和使用,適合初學者。 2)擁有龐大的開發者社區,資源豐富。 3)廣泛應用於WordPress、Drupal等平台。 4)與Web服務器緊密集成,簡化開發部署。

See all articles