首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 程式設計入門

動態 Proxy 與 Java ACL 實現細粒度存取控制

     編輯:Java 程式設計入門

從粗粒度到細粒度:存取控制的演進

傳統存取控制分為粗粒度(物件/目錄層級)與細粒度(方法/屬性層級)。早期使用靜態 Proxy 模式實現細粒度控制,需為每個業務類別建立對應 Proxy 類別(如 ForumProxy、ForumThreadProxy),導致類別爆炸、維護困難。Java 1.3 引入的動態 Proxy API 配合 java.security.acl 套件,提供了優雅的解決方案。

Java ACL 安全體系建立流程

  1. 建立主體:定義安全擁有者 Principal securityOwner = new PrincipalImpl("SecurityOwner")
  2. 用戶登入驗證:驗證帳密後,從資料庫載入該用戶對特定應用的權限集合
  3. 建立用戶主體:User user = new UserImpl(userId, new Hashtable())
  4. 創建 ACL 條目:將權限(讀/寫/刪)封裝為 Permission 物件,加入 AclEntry,最後綁定擁有者
// 核心 ACL 建立邏輯
AclEntry newAclEntry = new AclEntryImpl(user);
Enumeration keys = features.keys();
while (keys.hasMoreElements()) {
    String keyName = (String) keys.nextElement();
    Feature feature = (Feature) features.get(keyName); // Feature 繼承 Permission
    newAclEntry.addPermission(feature);
}
newAclEntry.setPrincipal(securityOwner); // 設定擁有者
acl.addEntry(securityOwner, newAclEntry);

動態 Proxy 實現方法級攔截

定義統一業務介面,透過 InvocationHandler 統一攔截所有方法呼叫,根據方法名稱前綴(get/set/is)判斷操作類型,委派 ACL 檢查權限。

public class ReportProxy implements InvocationHandler {
    private Map dataMap;
    private Acl acl;
    private Principal user;

    public static Object newInstance(Map dataMap, Class[] interfaces, Acl acl, Principal user) {
        return Proxy.newProxyInstance(
            dataMap.getClass().getClassLoader(),
            interfaces,
            new ReportProxy(dataMap, acl, user)
        );
    }

    private ReportProxy(Map dataMap, Acl acl, Principal user) {
        this.dataMap = dataMap;
        this.acl = acl;
        this.user = user;
    }

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        String methodName = method.getName();
        
        // 讀取操作:get*/is*
        if (methodName.startsWith("get") || methodName.startsWith("is")) {
            if (!acl.checkPermission(user, new Feature("read"))) {
                throw new SecurityException("無讀取權限");
            }
            String propName = methodName.substring(methodName.indexOf("get") + 3);
            if (methodName.startsWith("is")) propName = methodName.substring(2);
            return dataMap.get(propName);
        }
        
        // 寫入操作:set*
        if (methodName.startsWith("set")) {
            if (!acl.checkPermission(user, new Feature("write"))) {
                throw new SecurityException("無寫入權限");
            }
            String propName = methodName.substring(3);
            dataMap.put(propName, args[0]);
            return null;
        }
        
        return null; // 其他方法預設拒絕
    }
}

使用範例:角色基礎存取控制

// 定義業務介面
public interface IReport {
    String getName();
    void setName(String name);
    String getContent();
    void setContent(String content);
    boolean isPublished();
    void setPublished(boolean published);
}

// 建立 Proxy 實例
Map reportData = new HashMap<>();
reportData.put("Name", "季度報表");
reportData.put("Content", "業績數據...");
reportData.put("Published", false);

IReport report = (IReport) ReportProxy.newInstance(
    reportData,
    new Class[]{IReport.class},
    systemAcl,
    currentUser
);

// 一般用戶:僅能讀取
System.out.println(report.getName());        // 允許
report.setName("新名稱");                     // 拋出 SecurityException

// 管理者:可讀寫(ACL 中具備 write 權限)
report.setContent("更新內容");                 // 允許

架構優勢總結

維度靜態 Proxy動態 Proxy + ACL
類別數量每業務類一個 Proxy 類單一通用 Proxy 處理所有介面
粒度控制類別層級方法/屬性層級
權限管理硬編碼或分散配置集中式 ACL,支援動態授權
擴充性新增業務需新增 Proxy新增介面即可自動支援

實務部署建議

  • 快取 ACL 決策:高頻存取場景下,快取 checkPermission 結果減少 ACL 查詢開銷
  • 整合 JAAS:生產環境建議結合 Java Authentication and Authorization Service (JAAS) 統一身份認證
  • 審計日誌:在 invoke 中記錄存取嘗試(成功/失敗),滿足合規需求
  • 效能監控:動態 Proxy 會帶來微小反射開銷,極高頻呼叫場景需基準測試

動態 Proxy 與 ACL 的結合,以極小的程式碼量實現了企業級的細粒度存取控制,是 Java 安全架構設計的經典範式。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved