gpt4 book ai didi

java - 使用注释时忽略 `web.xml` 中的过滤器顺序?

转载 作者:行者123 更新时间:2023-11-28 22:47:26 25 4
gpt4 key购买 nike

简单的过滤器链(帖子底部的完整代码)。

假设我有一个提供用户名和密码的登录页面。

请求通过一个身份验证过滤器,该过滤器检查凭据,如果检查通过,则将用户对象作为属性添加到请求中。

@WebFilter(filterName = "AuthFilter",urlPatterns = "/secret")
public class AuthFilter implements Filter {...}

然后请求通过一个警告过滤器,该过滤器将采用该属性并使用它来记录用户访问该组件。

@WebFilter(filterName = "SecurityWarningFilter",urlPatterns = "/secret")
public class SecurityWarningFilter implements Filter { ... }

我现在一直试图通过故意以错误的顺序连接它们来强制执行 NPE。所以 SecurityWarningFilter应先处理请求,尝试操作尚未存在的属性并抛出异常。

我看过How to define servlet filter order of execution using annotations in WAR自从

The filters are invoked in the order in which filter mappings appear in the filter mapping list of a WAR. ~Servlet Tutorial

这就是我输入 web.xml 的内容:

<filter-mapping>
<filter-name>SecurityWarningFilter</filter-name>
<url-pattern />
</filter-mapping>

<filter-mapping>
<filter-name>AuthFilter</filter-name>
<url-pattern />
</filter-mapping>

不过,这并没有做任何事情。 AuthFilter仍然首先处理请求,并且只有当它把它传递给链时才会执行 SecurityWarningFilter开始做它的事情。

这是为什么呢?我该如何强制执行 NPE?

请注意,如果我注释掉注释并转而使用完整的 xml 定义:

<filter>
<filter-name>AuthFilter</filter-name>
<filter-class>[...].webapp.filters.AuthFilter</filter-class>
</filter>
<filter>
<filter-name>SecurityWarningFilter</filter-name>
<filter-class>[...].webapp.filters.SecurityWarningFilter</filter-class>
</filter>

<filter-mapping>
<filter-name>SecurityWarningFilter</filter-name>
<url-pattern>/secret</url-pattern>
</filter-mapping>

<filter-mapping>
<filter-name>AuthFilter</filter-name>
<url-pattern>/secret</url-pattern>
</filter-mapping>

然后我得到了我正在寻找的 NPE。 (并且翻转我定义 <filter-mapping> 标签的顺序再次摆脱它。)但我更愿意为过滤器定义使用注释,而不是 <filter>标签。

我正在使用 Apache Tomcat/7.0.47 .任何帮助将不胜感激。

(另外,祝大家圣诞快乐。)

更新似乎如果我在 xml 中提到 url 模式,我可以在使用注释时强制 NPE:

<web-app
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
version="3.0" metadata-complete="false"
>
<!-- this mapping forces an NPE -->
<filter-mapping>
<filter-name>SecurityWarningFilter</filter-name>
<url-pattern>/secret</url-pattern>
</filter-mapping>

<filter-mapping>
<filter-name>AuthFilter</filter-name>
<url-pattern>/secret</url-pattern>
</filter-mapping>

<!-- other servlets here -->
</web-app>

这向我表明关于 <url-pattern /> 的一些事情这需要一些配置——所以它从注释中获取 url 模式——我还没有完成。

有什么想法吗?

代码:

登录.jsp

<html>
<body>
<form action='/webapp/secret' method='post'>
username: <input type='text' name ='username'><br>
password: <input type='password' name ='password'><br>
<input type='submit', value='login'>
</form>
</body>
</html>

web.xml

<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd" >

<web-app>
<display-name>Webapp</display-name>
<filter-mapping>
<filter-name>SecurityWarningFilter</filter-name>
<url-pattern />
</filter-mapping>

<filter-mapping>
<filter-name>AuthFilter</filter-name>
<url-pattern />
</filter-mapping>
<!-- other servlets here -->
</web-app>

编辑:根据史蒂夫的建议(谢谢),这已更新为

新的 web.xml

<web-app
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
version="3.0" metadata-complete="false"
>
<!-- this mapping forces an NPE -->
<filter-mapping>
<filter-name>SecurityWarningFilter</filter-name>
<url-pattern />
</filter-mapping>

<filter-mapping>
<filter-name>AuthFilter</filter-name>
<url-pattern />
</filter-mapping>

<!-- other servlets here -->
</web-app>

AuthFilter.java

package [...].webapp.filters;

import [...].security.Credentials;
import [...].webapp.consts.AuthConstants;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.HashSet;
import java.util.Set;

@WebFilter(filterName = "AuthFilter",urlPatterns = "/secret")
public class AuthFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {}

@Override
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException {
String username = req.getParameter("username");
String password = req.getParameter("password");

if(username == null || password == null){
PrintWriter out = resp.getWriter();
out.println("access denied");
return;
}

Credentials creds = new Credentials(username,password, false);
if(validate(creds)){
req.setAttribute(AuthConstants.ATTR_ACTIVE_USER,creds);
chain.doFilter(req,resp);
} else{
PrintWriter out = resp.getWriter();
out.println("username or pasword is incorrect");
}
}

private boolean validate(Credentials creds){
Set<Credentials> acceptedUsers = getAcceptedUsers();
return acceptedUsers.contains(creds);
}

private Set<Credentials> getAcceptedUsers(){
//imagine a proper fetch, e.g. from DB or some cache, here
return new HashSet<Credentials>(){{add(new Credentials("foo","bar", false));}};
}

@Override
public void destroy() {}

}

SecurityWarningFilter.java

package [...].webapp.filters;

import [...].security.Credentials;
import [...].webapp.consts.AuthConstants;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;
import java.util.Date;

@WebFilter(filterName = "SecurityWarningFilter",urlPatterns = "/secret")
public class SecurityWarningFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {}

@Override
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException {
Credentials accessingUser = (Credentials)req.getAttribute(AuthConstants.ATTR_ACTIVE_USER);
doSecurityWarning(accessingUser);
chain.doFilter(req,resp);
}

private void doSecurityWarning(Credentials accessingUser) {
String timestamp = new Date().toString();

//imagine some proper logging, here
System.err.println(String.format("WARNING[%s] access to secured resource by user '%s'",timestamp,accessingUser.username));
}

@Override
public void destroy() {}
}

SecretServlet.java

package [...].webapp.servlets;

import [...].security.Credentials;
import [...].webapp.consts.AuthConstants;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.*;
import java.io.IOException;

@WebServlet("/secret")
public class SecretServlet extends HttpServlet {
@Override
public void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
serveRequest(req,resp);
}

@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
serveRequest(req, resp);
}

private void serveRequest(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
Credentials authorisedUser = (Credentials)req.getAttribute(AuthConstants.ATTR_ACTIVE_USER);
resp.getWriter().println(String.format("You are authorised. Welcome %s.",authorisedUser.username));
}
}

Credentials.java

package [...].security;

import javax.xml.bind.annotation.adapters.HexBinaryAdapter;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Objects;

public class Credentials{
public final String username;
final String password;

public Credentials(String username, String password, boolean isPasswordHashed) {
this.username = username;

if(isPasswordHashed) this.password = password;
else {
MessageDigest md;
try {
md = MessageDigest.getInstance("SHA-256");
} catch (NoSuchAlgorithmException e) {
throw new IllegalStateException(e);
}

md.update(password.getBytes());
byte[] hash = md.digest();

this.password = (new HexBinaryAdapter()).marshal(hash);
}
}

@Override
public boolean equals(Object obj) {
if(obj == null) return false;
if(!(obj instanceof Credentials)) return false;
Credentials other = (Credentials)obj;
return this.username.equals(other.username) && this.password.equals(other.password);
}

@Override
public int hashCode() {
return Objects.hash(username,password);
}

@Override
public String toString() {
return String.format("[\n\t%s\n\t%s\n]", username,password);
}
}

最佳答案

我认为在任何 servlet 规范(最高 4.0)下都不可能实现您想要的。

filter-mapping 的 XSD元素包含以下内容:

  <xsd:choice minOccurs="1"
maxOccurs="unbounded">
<xsd:element name="url-pattern"
type="javaee:url-patternType"/>
<xsd:element name="servlet-name"
type="javaee:servlet-nameType"/>
</xsd:choice>

这表示过滤器映射必须至少包含 url-pattern 之一或 servlet-name .

此外,这:

  <url-pattern />

相当于:

  <url-pattern></url-pattern>

规范 (§12.2) 指出:

The empty string ("") is a special URL pattern that exactly maps to the application's context root...

换句话说 <url-pattern />将始终覆盖您在 @WebFilter 中声明的任何模式注释,因为 XML 声明总是取代注释。

因此,如果您需要特定的过滤器排序,那么您必须声明完整的 filter-mapping元素包括正确的 url-pattern web.xml 中所需顺序的元素。

顺便说一句,您的 web.xml 部署描述符的 header 适用于更旧版本的 servlet 规范。

对于 Tomcat 7.x,它需要:

<web-app xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
version="3.0" metadata-complete="false" >

metadata-complete="false"实际上是默认值。

关于java - 使用注释时忽略 `web.xml` 中的过滤器顺序?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53914625/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com