过滤器(注解)
1.定义过滤器TestFilter001 ,实现Filter,重写init(初始化),doFilter(过滤),destroy(销毁)方法。
这里直接用@WebFilter就可以进行配置,同样,可以设置url匹配模式,过滤器名称等。
1.@WebFilter注解,filterName属性表示filter的名称,urlPatter表示要拦截的URL资源,可以是一个或者多个。
2.@Order(1)表示如果有多个拦截器的话就是设置这个拦截器的运行等级,数字越小,越先执行
3.init()方法只会执行一次,初始化过滤器。
4.doFilter()核心方法,配置过滤器的逻辑代码。
5.destroy()只会在项目停止或者是项目重新部署的时候才会执行。
配置完上面的之后我们还需要在启动类加上一个扫描包的注解,开启包扫描。@ServletComponentScan(“com.example.demofilter.filter”),当然你也可以不用写包的具体地址,不传参数,但是建议是传参数,并且这个采参数也可以传多个的。
import org.springframework.core.annotation.Order;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;
@Order(1)
@WebFilter(filterName = "myFilter1", urlPatterns = {"/*"})
public class TestFilter001 implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("初始化过滤器");
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("进入目标资源之前先干点啥");
// filterChain.doFilter(servletRequest, servletResponse);
long start = System.currentTimeMillis();
filterChain.doFilter(servletRequest,servletResponse);
System.out.println("Execute cost="+(System.currentTimeMillis()-start));
System.out.println("处理一下服务端返回的response");
}
@Override
public void destroy() {
System.out.println("过滤器被销毁了");
}
}
2.配置
这里需要注意一点的是@WebFilter这个注解是Servlet3.0的规范,并不是Spring boot提供的。除了这个注解以外,我们还需在配置类中加另外一个注解:@ServletComponetScan,指定扫描的包。
过滤器(非注解)
1. 现在我们通过过滤器来实现记录请求执行时间的功能,其实现如下:
public class LogCostFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
long start = System.currentTimeMillis();
filterChain.doFilter(servletRequest,servletResponse);
System.out.println("Execute cost="+(System.currentTimeMillis()-start));
}
@Override
public void destroy() {
}
}
2. 方法执行前先记录时间戳,然后通过过滤器链完成请求的执行,在返回结果之间计算执行的时间。这里需要主要,这个类必须继承Filter类,这个是Servlet的规范,这个跟以前的Web项目没区别。但是,有了过滤器类以后,以前的web项目可以在web.xml中进行配置,但是spring boot项目并没有web.xml这个文件, 在Spring boot中,我们需要FilterRegistrationBean来完成配置。其实现过程如下:
通过自己定义一个java配置文件,将自定义的Filter 注册到到FilterRegistrationBean里面。
@Configuration
public class FilterConfig {
@Bean
public FilterRegistrationBean registFilter() {
FilterRegistrationBean registration = new FilterRegistrationBean();
registration.setFilter(new LogCostFilter());
registration.addUrlPatterns("/*");
registration.setName("LogCostFilter");
registration.setOrder(1);
return registration;
}
}
这样配置就完成了,需要配置的选项主要包括实例化Filter类,然后指定url的匹配模式,设置过滤器名称和执行顺序,这个过程和在web.xml中配置其实没什么区别,只是形式不同而已。现在我们可以启动服务器访问任意URL。
拦截器
1.实现拦截器类
这里我们需要实现HandlerInterceptor这个接口,这个接口包括三个方法,preHandle是请求执行前执行的,postHandler是请求结束执行的,但只有preHandle方法返回true的时候才会执行,afterCompletion是视图渲染完成后才执行,同样需要preHandle返回true,该方法通常用于清理资源等工作
public class TestInterceptor implements HandlerInterceptor {
private static ThreadLocal<Long> e = new ThreadLocal<>();
@Override
public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception {
e.set(System.currentTimeMillis());
return true;
}
@Override
public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {
System.out.println("Interceptor cost=" + (System.currentTimeMillis() - e.get()));
}
@Override
public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {
}
}
2.配置拦截器
这里我们继承了WebMVCConfigurerAdapter,看过前面的文章的朋友应该已经见过这个类了,在进行静态资源目录配置的时候我们用到过这个类。这里我们重写了addInterceptors这个方法,进行拦截器的配置,主要配置项就两个,一个是指定拦截器,第二个是指定拦截的URL
@Configuration
public class TestInterceptorConfig extends WebMvcConfigurerAdapter {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new TestInterceptor()).addPathPatterns("/**");
super.addInterceptors(registry);
}
}
总结:
在分析两者的区别之前,我们先理解一下AOP的概念,AOP不是一种具体的技术,而是一种编程思想。在面向对象编程的过程中,我们很容易通过继承、多态来解决纵向扩展。 但是对于横向的功能,比如,在所有的service方法中开启事务,或者统一记录日志等功能,面向对象的是无法解决的。所以AOP——面向切面编程其实是面向对象编程思想的一个补充。而我们今天讲的过滤器和拦截器都属于面向切面编程的具体实现。而两者的主要区别包括以下几个方面:
1、Filter是依赖于Servlet容器,属于Servlet规范的一部分,而拦截器则是独立存在的,可以在任何情况下使用。
2、Filter的执行由Servlet容器回调完成,而拦截器通常通过动态代理的方式来执行。
3、Filter的生命周期由Servlet容器管理,而拦截器则可以通过IoC容器来管理,因此可以通过注入等方式来获取其他Bean的实例,因此使用会更方便。
无论是过滤器还是拦截器都属于AOP(面向切面编程)思想的具体实现。除了这两种实现我们还见过另一种更灵活的AOP实现技术,即Aspect,我们可以通过Aspect来完成更多更强大的功能。
++
/**
* xss攻击过滤器
*/
@Slf4j
@Component
@WebFilter(urlPatterns = "/*", filterName = "XSSCheck", initParams = { @WebInitParam(name = "securityconfig", value = "/*") })
@ConditionalOnProperty(value = "hysoft.filter.xss.enable", havingValue = "true", matchIfMissing = false)
public class XssFilter implements SafeFilter {
private FilterConfig config;
private static String errorPath;
private static String[] excludePaths;
private static String[] safeless;
public final static String XSS_ERROR_PATH = "/templates/common/error.ftl";
public final static String XSS_EXCLUDE_PATHS = "";
public final static String XSS_SAFELESS = "<script, </script, <iframe, </iframe, <frame, </frame, set-cookie, %3cscript, %3c/script, %3ciframe, %3c/iframe, %3cframe, %3c/frame, src=\"javascript:, <body, </body, %3cbody, %3c/body, <, >, </, />, %3c, %3e, %3c/, /%3e";
@Override
public void init(FilterConfig config) throws ServletException {
this.config = config;
try {
errorPath = XSS_ERROR_PATH;
String excludePath = XSS_EXCLUDE_PATHS;
if (!"".equals(excludePath) && null != excludePath){
excludePaths = excludePath.split(",");
}
String safeles = XSS_SAFELESS;
if (!"".equals(safeles) && null != safeles) {
safeless = safeles.split(",");
log.debug(safeless.toString());
}
} catch (Exception e) {
log.error("XSS初始化失败",e);
}
}
@Override
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain filterChain) throws IOException, ServletException {
Enumeration params = req.getParameterNames();
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) resp;
boolean isSafe = true;
String requestUrl = request.getRequestURI();
if (isSafe(requestUrl)) {
requestUrl = requestUrl.substring(requestUrl.indexOf("/"));
if (!excludeUrl(requestUrl)) {
String cache = "";
while (params.hasMoreElements()) {
cache = req.getParameter((String) params.nextElement());
if (!"".equals(cache) && null != cache) {
if (!isSafe(cache)) {
isSafe = false;
break;
}
}
}
}
} else {
isSafe = false;
}
if (!isSafe) {
log.error("您输入的参数有非法字符,请输入正确的参数!");
request.setAttribute("err", "您输入的参数有非法字符,请输入正确的参数!");
request.setAttribute("pageUrl",request.getRequestURI());
request.getRequestDispatcher(errorPath).forward(request, response);
return;
}
filterChain.doFilter(req, resp);
}
private static boolean isSafe(String str) {
if (!"".equals(str) && null != str) {
for (String s : safeless) {
if (str.toLowerCase(Locale.ENGLISH).contains(s)){
return false;
}
}
}
return true;
}
private boolean excludeUrl(String url) {
if (excludePaths != null && excludePaths.length > 0) {
for (String path : excludePaths) {
if (url.toLowerCase(Locale.ENGLISH).equals(path)){
return true;
}
}
}
return false;
}
@Override
public void destroy() {
}
}
++
/**
* 1.拦截用户登陆信息
**/
@Component
public class AuthRestInterceptor extends HandlerInterceptorAdapter {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
HandlerMethod handlerMethod = (HandlerMethod) handler;
String clazzName = handlerMethod.getBeanType().getName();
HttpSession session = request.getSession();
UserAccountVo vo = (UserAccountVo) session.getAttribute("xxx.userinfo");
if(vo != null){
BaseContextHandler.setUser(new User(vo.getUserId(),vo.getUsername(),vo.getLoginId(),vo.getSex(),vo.getOrgId(),vo.getOrgName(),vo.getOrgType(),vo.getOrgLevel(),vo.getAreaId(),vo.getAreaName(),vo.getAreaCode(),vo.getRoles(), Integer.valueOf(vo.getAreaLevel()) ));
}
return super.preHandle(request, response, handler);
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
BaseContextHandler.remove();
super.afterCompletion(request, response, handler, ex);
}
}
```java
/**
*2.配置
**/
@Configuration
public class WebMvcConfig extends WebMvcConfigurerAdapter {
@Autowired
private List<HandlerMethodArgumentResolver> resolvers;
@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
argumentResolvers.addAll(0, resolvers);
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(getAuthRestInterceptor()).addPathPatterns("/**")
.excludePathPatterns(getExcludeCommonPathPatterns().toArray(new String[]{}));
super.addInterceptors(registry);
}
@Bean
AuthRestInterceptor getAuthRestInterceptor() {
return new AuthRestInterceptor();
}
private ArrayList<String> getExcludeCommonPathPatterns() {
ArrayList<String> list = new ArrayList<>();
String[] urls = {
"/error",
"/swagger-resources/**",
"/swagger-ui.html**",
"/webjars/**",
"/loginRestService/getConfig",
"/loginRestService/changePassword",
"/loginRestService/checkUser",
"/loginRestOverwriteService/changePassword",
"/CaptchaImg**"
};
Collections.addAll(list, urls);
return list;
}
}
/**
*存储用户信息
**/
public class BaseContextHandler {
public static final ThreadLocal<Map<String, Object>> threadLocal = new ThreadLocal<>();
/** 临时存在ThreadLocal中的用户 key */
public static final String CONTEXT_KEY_USER = "currentUser";
private static void set(String key, Object value) {
Map<String, Object> map = threadLocal.get();
if (map == null) {
map = new HashMap<>();
threadLocal.set(map);
}
map.put(key, value);
}
private static Object get(String key) {
Map<String, Object> map = threadLocal.get();
if (map == null) {
map = new HashMap<>();
threadLocal.set(map);
}
return map.get(key);
}
/**
* 获取用户
* @return
*/
public static User getUser() {
Object value = get(CONTEXT_KEY_USER);
if (value != null && !"".equals(value)) {
return (User) value;
}
User user = new User();
user.setUserId("");
user.setOrgId("");
// 预防空指针
return user;
}
public static void setUser(User user) {
set(CONTEXT_KEY_USER, user);
}
public static void remove() {
threadLocal.remove();
}
}