You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

在Servlet/Filter中如何获取web.xml中无auth-constraint的URL模式列表?

获取无auth-constraint的security-constraint对应URL列表的方法

这个问题我之前也琢磨过,Servlet标准API并没有直接提供读取security-constraint配置的接口,不过有两种可行的思路,一种是跨容器的通用XML解析方式,另一种是借助容器特定API快速获取,下面给你详细拆解:

一、通用跨容器方式:手动解析部署描述符

这种方式不依赖具体容器,能在Tomcat、Jetty、GlassFish等所有Servlet容器中运行,核心是自己解析web.xml以及可能存在的web-fragment.xml文件,筛选出没有配置auth-constraint的security-constraint下的url-pattern。

实现步骤(以Filter为例)

在Filter的init方法中完成解析(只执行一次,避免重复解析影响性能):

  1. 获取web.xml的输入流,同时处理WEB-INF/lib目录下jar包中的web-fragment.xml
  2. 用XML解析工具(比如DOM)遍历所有security-constraint元素
  3. 判断每个security-constraint是否包含auth-constraint,如果没有则提取对应的url-pattern

示例代码

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import org.w3c.dom.*;
import javax.xml.parsers.*;
import java.io.*;
import java.util.*;
import java.util.jar.*;

@WebFilter(filterName = "UnprotectedUrlFilter", urlPatterns = "/*")
public class UnprotectedUrlFilter implements Filter {
    private List<String> unprotectedUrlPatterns;

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        unprotectedUrlPatterns = new ArrayList<>();
        ServletContext context = filterConfig.getServletContext();

        // 解析主web.xml文件
        parseDeploymentDescriptor(context.getResourceAsStream("/WEB-INF/web.xml"));

        // 解析WEB-INF/lib中jar包的web-fragment.xml
        Set<String> libResources = context.getResourcePaths("/WEB-INF/lib/");
        if (libResources != null) {
            for (String libPath : libResources) {
                if (libPath.endsWith(".jar")) {
                    try (JarInputStream jarStream = new JarInputStream(context.getResourceAsStream(libPath))) {
                        JarEntry entry;
                        while ((entry = jarStream.getNextJarEntry()) != null) {
                            if ("META-INF/web-fragment.xml".equals(entry.getName())) {
                                parseDeploymentDescriptor(jarStream);
                                break;
                            }
                        }
                    } catch (IOException e) {
                        // 这里可以根据业务需求做异常处理,比如打日志
                        e.printStackTrace();
                    }
                }
            }
        }

        // 将解析结果存储到ServletContext,方便后续使用
        context.setAttribute("unprotectedUrls", unprotectedUrlPatterns);
    }

    private void parseDeploymentDescriptor(InputStream inputStream) {
        if (inputStream == null) return;

        try {
            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
            DocumentBuilder builder = factory.newDocumentBuilder();
            Document doc = builder.parse(inputStream);
            doc.getDocumentElement().normalize();

            // 遍历所有security-constraint元素
            NodeList constraintNodes = doc.getElementsByTagName("security-constraint");
            for (int i = 0; i < constraintNodes.getLength(); i++) {
                Node constraintNode = constraintNodes.item(i);
                if (constraintNode.getNodeType() == Node.ELEMENT_NODE) {
                    Element constraintElement = (Element) constraintNode;

                    // 检查当前security-constraint是否包含auth-constraint
                    NodeList authConstraintNodes = constraintElement.getElementsByTagName("auth-constraint");
                    if (authConstraintNodes.getLength() == 0) {
                        // 提取所有url-pattern
                        NodeList urlPatternNodes = constraintElement.getElementsByTagName("url-pattern");
                        for (int j = 0; j < urlPatternNodes.getLength(); j++) {
                            String urlPattern = urlPatternNodes.item(j).getTextContent().trim();
                            unprotectedUrlPatterns.add(urlPattern);
                        }
                    }
                }
            }
        } catch (ParserConfigurationException | SAXException | IOException e) {
            e.printStackTrace();
        } finally {
            try {
                inputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        // 这里可以使用unprotectedUrlPatterns做你需要的特定操作
        chain.doFilter(request, response);
    }

    @Override
    public void destroy() {
        // 清理资源
        unprotectedUrlPatterns.clear();
    }
}

注意:要区分“没有auth-constraint元素”和“有auth-constraint但未配置角色”两种情况——后者实际上是拒绝所有用户访问,不属于无保护的URL,解析时要注意判断条件。

二、容器特定方式:利用容器内部API

如果你的应用固定运行在某个Servlet容器(比如Tomcat),可以直接调用容器提供的内部API来获取security-constraint配置,这种方式更简洁,但跨容器兼容性差。

以Tomcat为例的示例代码

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import org.apache.catalina.core.ApplicationContextFacade;
import org.apache.catalina.core.StandardContext;
import org.apache.catalina.deploy.SecurityConstraint;
import java.lang.reflect.Field;
import java.util.*;

@WebFilter(filterName = "TomcatUnprotectedUrlFilter", urlPatterns = "/*")
public class TomcatUnprotectedUrlFilter implements Filter {
    private List<String> unprotectedUrlPatterns;

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        unprotectedUrlPatterns = new ArrayList<>();
        ServletContext context = filterConfig.getServletContext();

        // 借助反射获取Tomcat的StandardContext
        if (context instanceof ApplicationContextFacade) {
            try {
                Field contextField = ApplicationContextFacade.class.getDeclaredField("context");
                contextField.setAccessible(true);
                StandardContext standardContext = (StandardContext) contextField.get(context);

                // 获取所有security-constraint配置
                SecurityConstraint[] constraints = standardContext.findConstraints();
                for (SecurityConstraint constraint : constraints) {
                    // Tomcat中,没有auth-constraint的SecurityConstraint的getAuthConstraint()返回null
                    if (constraint.getAuthConstraint() == null) {
                        String[] urlPatterns = constraint.findUrlPatterns();
                        unprotectedUrlPatterns.addAll(Arrays.asList(urlPatterns));
                    }
                }
            } catch (NoSuchFieldException | IllegalAccessException e) {
                e.printStackTrace();
            }
        }

        context.setAttribute("unprotectedUrls", unprotectedUrlPatterns);
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        // 执行你的特定操作
        chain.doFilter(request, response);
    }

    @Override
    public void destroy() {
        unprotectedUrlPatterns.clear();
    }
}

注意:这种方式依赖Tomcat的内部实现,版本升级可能导致API变化,使用前要做好兼容性测试;其他容器(如Jetty)的API会不同,需要单独适配。

内容的提问来源于stack exchange,提问作者Mohamed_bilal

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.13 08:09:42