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

基於自定義標簽庫與 XML 配置的 JSP 頁面元素級權限控制實現

     編輯:關於JSP

背景痛點

傳統 web.xml 安全約束僅能控制整個 URL/文件級訪問,無法實現「同一頁面中,按鈕 A 管理員可見,按鈕 B 普通用戶可見」這種元素級細粒度權限。硬編碼 if (user.hasRole("admin")) 會導致業務邏輯與視圖耦合、維護成本高。

解決方案架構

┌─────────────┐     ┌──────────────┐     ┌─────────────────┐
│  JSP 頁面   │────▶│ 自定義標簽   │────▶│  XML 權限配置   │
│ │     │ JspSecurityTag│     │ security.xml    │
└─────────────┘     └──────────────┘     └─────────────────┘
       ▲                    │                      ▲
       │              讀取 Session              角色-元素映射
       └────────────────────┴──────────────────────┘

核心組件實現

1. 標簽處理類 JspSecurityTag

package com.presentation.viewhelper;

import javax.servlet.jsp.tagext.*;
import javax.servlet.jsp.*;
import java.util.*;
import org.w3c.dom.*;
import javax.xml.parsers.*;

public class JspSecurityTag extends BodyTagSupport {
    // 靜態緩存:角色-元素映射表
    private static List roleList;
    // 標簽屬性:受保護元素唯一名稱
    private String elementName;

    public void setElementName(String name) { this.elementName = name; }

    @Override
    public int doAfterBody() throws JspException {
        // 懶加載配置
        if (roleList == null) roleList = loadRoleMapping();
        
        try {
            // 從 Session 獲取當前用戶角色
            String role = (String) pageContext.getSession().getAttribute("rolename");
            // 驗證權限
            if (isAuthorized(elementName, role)) {
                // 輸出標簽體內容(受保護的 HTML 片段)
                if (bodyContent != null) {
                    bodyContent.writeOut(bodyContent.getEnclosingWriter());
                }
            }
        } catch (Exception e) {
            throw new JspException(e);
        }
        return SKIP_BODY;
    }

    // 解析 XML 配置
    private List loadRoleMapping() {
        List list = new ArrayList<>();
        try {
            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
            DocumentBuilder builder = factory.newDocumentBuilder();
            Document doc = builder.parse(new File("security.xml")); // 生產環境建議用 getResourceAsStream
            
            NodeList elements = doc.getElementsByTagName("htmlElement");
            for (int i = 0; i < elements.getLength(); i++) {
                Element el = (Element) elements.item(i);
                String elemName = el.getAttribute("name");
                NodeList roles = el.getElementsByTagName("roleName");
                for (int j = 0; j < roles.getLength(); j++) {
                    String roleName = ((Element) roles.item(j)).getAttribute("name");
                    list.add(new ElementAndRole(elemName, roleName));
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return list;
    }

    // 權限判斷
    private boolean isAuthorized(String elem, String role) {
        return roleList.contains(new ElementAndRole(elem, role));
    }

    // 內部類:實現 equals/hashCode 供 contains 使用
    static class ElementAndRole {
        final String elementName, roleName;
        ElementAndRole(String e, String r) { elementName = e; roleName = r; }
        @Override public boolean equals(Object o) {
            if (!(o instanceof ElementAndRole)) return false;
            ElementAndRole other = (ElementAndRole) o;
            return elementName.equals(other.elementName) && roleName.equals(other.roleName);
        }
        @Override public int hashCode() { return Objects.hash(elementName, roleName); }
    }
}

2. XML 權限配置 security.xml

<?xml version="1.0" encoding="GB2312"?>
<security>
    <htmlElement name="employeedetail">
        <roleName name="common"/>
        <roleName name="admin"/>
    </htmlElement>
    <htmlElement name="employeemodify">
        <roleName name="admin"/>
    </htmlElement>
</security>

3. TLD 文件 mytag.tld

<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE taglib PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.1//EN"
  "https://java.sun.com/j2ee/dtds/web-jsptaglibrary_1_1.dtd">
<taglib>
    <tlibversion>1.0</tlibversion>
    <jspversion>1.1</jspversion>
    <shortname>myTag</shortname>
    <tag>
        <name>JspSecurity</name>
        <tagclass>com.presentation.viewhelper.JspSecurityTag</tagclass>
        <bodycontent>JSP</bodycontent>
        <attribute>
            <name>elementName</name>
            <required>true</required>
            <rtexprvalue>true</rtexprvalue>
        </attribute>
    </tag>
</taglib>

4. JSP 頁面使用示例

<%@ taglib uri="https://mytag" prefix="custTag" %>
<html><body>
<form>
    <custTag:JspSecurity elementName="employeedetail">
        <input type="button" value="詳細" />
    </custTag:JspSecurity>
    
    <custTag:JspSecurity elementName="employeemodify">
        <input type="button" value="修改" />
    </custTag:JspSecurity>
</form>
</body></html>

5. web.xml 註冊

<taglib>
    <taglib-uri>https://mytag</taglib-uri>
    <taglib-location>/WEB-INF/mytag.tld</taglib-location>
</taglib>

部署檢查清單

  1. security.xml 放置於類路徑根目錄(或改用 getResourceAsStream 讀取)。
  2. 標簽類編譯後置於 WEB-INF/classes 或打包進 JAR。
  3. mytag.tld 置於 WEB-INF/。
  4. 登入成功後務必 session.setAttribute("rolename", userRole)。

優化建議

  • 將 roleList 改為 Map>(元素→角色集合),查詢 O(1)。
  • 引入緩存框架(如 Ehcache)支援配置熱加載。
  • 結合 Spring Security 等成熟框架替代自研方案。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved