简单的过滤器链(文章底部的完整代码)。
假设我有一个提供用户名和密码的登录页面。
请求通过身份验证过滤器,该过滤器检查凭据,如果签出,则将用户对象作为属性添加到请求中。
@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以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
传递给链后,<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?
请注意,如果我注释掉了注释,而是使用了完整的xml定义:
<filter-mapping>
当我获得所需的NPE时。 (并且颠倒了我定义<filter>
标签的顺序,这又使它摆脱了。)
但是我非常希望对过滤器定义使用注释,而不是Apache Tomcat/7.0.47
标签。
我正在使用<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>
。
任何帮助将不胜感激。
(也祝大家圣诞快乐。)
更新 看来,如果我在xml中提到url模式,则可以在使用注释时强制使用NPE:
<url-pattern />
这向我表明<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>
上有一些东西需要一些配置-因此它需要使用注释中的url模式-我还没有做过。
有什么想法吗?
代码:
login.jsp
<!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>
EDIT :根据史蒂夫的建议(谢谢),此内容已更新为
新的 web.xml
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() {}
}
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.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() {}
}
SecurityWarningFilter.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));
}
}
SecretServlet.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);
}
}
Credentials.java
@Override
public void onBindViewHolder(final RecyclerViewHolder viewHolder, final int i) {
foodListDetail ft = foodList.get(i);
viewHolder.tvFood.setText(ft.getFood());
viewHolder.tvCost.setText(String.valueOf(ft.getCost())+" บาท");
viewHolder.tvunit.setText(ft.getUnit());
Picasso.get()
.load(ft.getUrl())
.placeholder(R.drawable.ic_android_black_24dp)
.error(R.drawable.ic_error_black_24dp)
.into(viewHolder.img);
viewHolder.cd.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
final Dialog myDialog = new Dialog(ctx);
myDialog.setContentView(R.layout.popup1);
ImageView img = myDialog.findViewById(R.id.imageView);
final TextView tvName = (TextView)myDialog.findViewById(R.id.foodName);
final TextView tvCost = (TextView)myDialog.findViewById(R.id.cost);
TextView tvClose = (TextView)myDialog.findViewById(R.id.tvClose);
TextView tvNum = (TextView)myDialog.findViewById(R.id.num);
ImageView up = myDialog.findViewById(R.id.up);
ImageView down = myDialog.findViewById(R.id.down);
myDialog.show();
}
});
}
答案 0 :(得分:1)
我认为在任何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节)规定:
空字符串(“”)是一种特殊的网址格式,它精确地映射到 应用程序的上下文根...
换句话说,<url-pattern />
将始终覆盖您在@WebFilter
批注中声明的任何模式,因为XML声明始终取代批注。
因此,如果您需要特定的过滤器排序,则必须在web.xml中以要求的顺序声明完整的filter-mapping
元素,包括正确的url-pattern
元素。
顺便说一句,您的web.xml部署描述符的标头用于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"
实际上是默认值。