开发过程中,碰到需要需要拦截过滤的需求,特此整理下的。
目前碰到的,也经常使用的有三种:Filter过滤器方式、Interceptor拦截器、AOP切面方式。后面两种在spring中常用。
1》 Filter:
filter的实现简单,属于servlet的 。可以实现javax.servlet.Filter即可。查看源码中,可以看到实现filter的有很多类,我们常用到的就用:
- org.springframework.web.filter.CharacterEncodingFilter
- org.springframework.web.multipart.support.MultipartFilter
等等。下面就用工作中碰到的具体业务为例说明:
SecurityUrlFilter:
这个过滤器是用于某些安全请求,某些请求路径必须是校验通过之后才能访问。
package com.zcl.filter;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebFilter;
import javax.servlet.annotation.WebInitParam;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.io.IOUtils;
import com.zcl.util.MD5Utils;
@WebFilter(displayName="filterName", urlPatterns={"/*"}, initParams={
@WebInitParam(name="openFilter", value="true"),
@WebInitParam(name="securityKey", value="1w3e$dr3"),
@WebInitParam(name="includeUrls", value="/security/delid.htm,/user/add.htm")})
public class SecurityUrlFilter implements Filter {
//是否开启过滤
private boolean openFilter = false;
//需要过滤的路径
private Set<String> includeUrls;
//安全key
private String securityKey;
public void init(FilterConfig filterConfig) throws ServletException {
//获取是否开启过滤
String openFilter = filterConfig.getInitParameter("openFilter");
this.openFilter = openFilter != null && "true".equals(openFilter.trim());
//获取路径
String urls = filterConfig.getInitParameter("includeUrls");
if(urls != null && !"".equals(urls.trim())) {
includeUrls = new HashSet<String>(Arrays.asList(urls.split("\\s*,\\s*")));
}
}
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
String requestUrl = ((HttpServletRequest)request).getRequestURI();
if(includeUrls != null && includeUrls.size() > 0 ) {
boolean securityCheck = false;
for(String url: includeUrls) {
if(requestUrl.matches(url)) {
securityCheck = true;
break;
}
}
if(securityCheck) {
String token = ((HttpServletRequest)request).getParameter("token");
if(token == null || "".equals(token.trim())) {
writerMsg(response.getWriter(), "必须传入token验证参数");
return;
}
if(!token.equals(MD5Utils.MD5(securityKey))) {
writerMsg(response.getWriter(), "token验证参数不正确");
return;
}
}
}
chain.doFilter(request, response);
}
private void writerMsg(PrintWriter writer, String msg) {
try {
writer.write(msg);
} catch(Exception e) {
} finally {
IOUtils.closeQuietly(writer);
}
}
public void destroy() {
// TODO Auto-generated method stub
}
}
初始化相关的实现在init(FilterConfig filterConfig)方法中实现;
功能处理在doFilter(ServletRequest request, ServletResponse response,
FilterChain chain)方法中实现。
注意,我使用的是servlet3.1的,支持注解方式。
@WebFilter(filterName="filterName", urlPatterns={"/*"}, initParams={
@WebInitParam(name="openFilter", value="true"),
@WebInitParam(name="securityKey", value="1w3e$dr3"),
@WebInitParam(name="includeUrls", value="/security/delid.htm,/user/add.htm")})
对于版本低的,是在配置web.xml文件中配置。
<filter>
<filter-name>filterName</filter-name>
<filter-class>com.zcl.filter.SecurityUrlFilter</filter-class>
<init-param>
<param-name>openFilter</param-name>
<param-value>true</param-value>
</init-param>
<init-param>
<param-name>securityKey</param-name>
<param-name>1w3e$dr3</param-name>
</init-param>
<init-param>
<param-name>includeUrls</param-name>
<param-value>
/security/delid.htm,
/user/add.htm
</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>filterName</filter-name>
<url-pattern>*</url-pattern>
</filter-mapping>
这是使用过滤器的方式。不过要注意他们的流程:
注意:filter的注解方式没有提供排序的参数。在开发中,可以通过filter的class名称字母来排序(filterName的字母排序是不起作用的)。
2》Interceptor:
拦截器是spring提供的过滤方式,最上层的是接口HandlerInterceptor。spring提供了很多它的实现类。平时项目中常用的是自定义class继承HandlerInterceptorAdapter。
package com.zcl.interceptor;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import com.zcl.constants.Constants;
import com.zcl.util.CSRFTokenManager;
/**
* CSRF防御拦截处理
*
* @ClassName: CSRFDefenseInterceptor
* @Description: TODO
* @author ****
* @date May 5, 2016 4:21:49 PM
*
*
*/
public class CSRFDefenseInterceptor extends HandlerInterceptorAdapter {
// 需要过滤的路径
private List<String> includedUrls;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
String requestMethod = request.getMethod();
// 拦截post方法
if (!"POST".equals(requestMethod))
return true;
String requestUrl = request.getRequestURI();
// 拦截的urls
if (includedUrls != null && includedUrls.size() > 0) {
boolean interceptFlag = false;
for (String url : includedUrls) {
if (requestUrl.matches(url)) {
interceptFlag = true;
break;
}
}
if (!interceptFlag)
return true;
// 不拦截的urls
}
boolean checkResult = CSRFTokenManager.checkToken(Constants.CSRF_TOKEN, Constants.SESSION_KEY,
request);
if (!checkResult) {
String toErrorUrl = "/error?errorCode=-1&errorMsg=Cross-site request forgery";
response.sendRedirect(request.getContextPath() + toErrorUrl);
}
return checkResult;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
// TODO Auto-generated method stub
super.postHandle(request, response, handler, modelAndView);
if (modelAndView != null) {
// 执行完成过后,页面添加csrfToken参数
String csrfToken = CSRFTokenManager.createCsrfToken(Constants.CSRF_TOKEN,
Constants.SESSION_KEY, request);
modelAndView.addObject(Constants.CSRF_TOKEN, csrfToken);
}
}
public List<String> getIncludedUrls() {
return includedUrls;
}
public void setIncludedUrls(List<String> includedUrls) {
this.includedUrls = includedUrls;
}
}
--------------------------------------------------------
package com.zcl.interceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.lang3.StringUtils;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import com.zcl.util.DESUtil;
public class ParamChangeInterceptor extends HandlerInterceptorAdapter {
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) throws Exception {
/**
* 参数过滤
*/
String secrityCode = request.getParameter("secrityCode");
if(StringUtils.isBlank(secrityCode))
return false;
secrityCode = DESUtil.encrypt(secrityCode);
request.setAttribute("secrityCode", secrityCode);
return super.preHandle(request, response, handler);
}
}
其中主要的是涉及的方法:
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception;
//此方式在请求执行前进行处理的,通过返回值来确定是否继续进入后续执行。
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception;
//此方法是执行方法,可以看出多了ModelAndView的参数,这样就拓展了对页面、页面参数等的处理。
void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
throws Exception;
//此方式是在请求处理完成之后,
同时这个拦截器需要在.xml中配置
<bean id="csrfInterceptor" class="com.zcl.interceptor.CSRFDefenseInterceptor" >
<property name="includedUrls">
<list>
<value>/log.htm</value>
</list>
</property>
</bean>
<mvc:interceptors>
<!-- csrf拦截过滤 -->
<mvc:interceptor>
<mvc:mapping path="/*"/>
<mvc:exclude-mapping path="/resource/*"/>
<ref bean="csrfInterceptor"/>
</mvc:interceptor>
<!-- secrityCode参数解码处理 -->
<mvc:interceptor>
<mvc:mapping path="/*"/>
<bean class="com.zcl.interceptor.ParamChangeInterceptor"></bean>
</mvc:interceptor>
</mvc:interceptors>
注意的是,拦截器的执行顺序是最先开始的最后执行完成。
拦截器与过滤器区别:
- 拦截器是基于Java的反射机制的,而过滤器是基于函数回调。
- 拦截器不依赖与servlet容器,过滤器依赖与servlet容器。
- 拦截器只能对action请求起作用,而过滤器则可以对几乎所有的请求起作用。
- 拦截器可以访问action上下文、值栈里的对象,而过滤器不能访问。
- 在action的生命周期中,拦截器可以多次被调用,而过滤器只能在容器初始化时被调用一次。
- 拦截器可以获取IOC容器中的各个bean,而过滤器就不行,这点很重要,在拦截器里注入一个service,可以调用业务逻辑。
- 他们都是AOP思想的实践。
他们的执行过程:
3》AspectJ: