@WebFilter
@WebFilter 用于将一个类声明为过滤器,该注解将会在部署时被容器处理,容器将根据具体的属性配置将相应的类部署为过滤器。该注解具有下表给出的一些常用属性 ( 以下所有属性均为可选属性,但是 value、urlPatterns、servletNames 三者必需至少包含一个,且 value 和 urlPatterns 不能共存,如果同时指定,通常忽略 value 的取值 ):

表 3. @WebFilter 的常用属性

下面是一个简单的示例:

@WebFilter(servletNames = {"SimpleServlet"},filterName="SimpleFilter")
public class LessThanSixFilter implements Filter{...}

如此配置之后,就可以不必在 web.xml 中配置相应的 <filter> 和 <filter-mapping> 元素了,容器会在部署时根据指定的属性将该类发布为过滤器。它等价的 web.xml 中的配置形式为:

<filter>
  <filter-name>SimpleFilter</filter-name>
  <filter-class>xxx</filter-class>
</filter>
<filter-mapping>
  <filter-name>SimpleFilter</filter-name>
  <servlet-name>SimpleServlet</servlet-name>
</filter-mapping>

由上文可知,使用@WebFilter相当于配置了web.xml,现在用eclipse自动生成Filter时,默认是提供这个注解的,如下所示

package webcase;

import java.io.IOException;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest; /**
* Servlet Filter implementation class CountFilter
*/
@WebFilter("/CountFilter")
public class CountFilter implements Filter {
private int count;
private String param;
private FilterConfig fConfig;
/**
* Default constructor.
*/
/*public CountFilter() {
// TODO Auto-generated constructor stub
}*/ /**
* @see Filter#destroy()
*/

我们知道,tomcat根据<filter-mapping>的顺序初始化Filter,由于上面的代码包含@WebFilter("/CountFilter"),相当于在web.xml中对同一个过滤器CountFilter设置了两次<filter>和<filter-mapping>,故这个过滤器会初始化两次,故当@WebFilter设置的过滤器被初始化时,String param=getInitParameter("count")得到的字符串为空,调用Integer.parseInt(param)时引发NumberFormatException异常。去掉@WebFilter("/CountFilter")后则一切正常。

采坑记录:

业务需求背景:
项目采用微服务架构,在各个服务前面配置一个网关,通过SpringCloud生态中的Zuul组件实现。
该网关同时负责页面调度,在各个单页面应用子产品的页面之间进行调度。
ZuulFilter挺有意思,对于本服务的Controller请求不会进行拦截,因此需要针对页面请求做一个认证鉴权的Filter。

实现第一版
首先实现一个Filter进行鉴权及页面重定向(未登录认证状态下跳转到登录页面)。
大体逻辑如下:
①通过WebFilter进行Filter声明,这样容器在进行部署的时候就会处理该Filter,创建实例并创建配置对象FilterConfig,然后会将该Filter应用到urlPatterns所指定的url;
②在init方法中获取到初始化参数,自定义的excludedUrls,作为成员在后续执行过滤逻辑的时候使用;
③在doFilter中进行url的鉴定,如果需要执行认证鉴权处理,则执行相应逻辑。不满足条件的情况下重定向到登录页;
④Filter类增加Component注解,让该Filter被容器管理。

@Component //这样加对吗?
@WebFilter(filterName = "WebAuthFilter", urlPatterns = "/web/*",
initParams = {
@WebInitParam(name = "excludedUrls", value = "/web/login")
}
)
public class WebAuthFilter implements Filter { private List<String> excludedUrlList; @Override
public void init(FilterConfig filterConfig) throws ServletException {
String excludedUrls = filterConfig.getInitParameter("excludeUrls");
excludedUrlList = Splitter.on(",").omitEmptyStrings().splitToList(excludedUrls);
} @Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
String url = ((HttpServletRequest) request).getRequestURI();
if (excludedUrlList.contains(url)) {
chain.doFilter(request, response);
} else {
String sToken = ((HttpServletRequest) request).getHeader("Authorization");
if (sToken != null) {
Map<String, Object> map = TokenUtils.parseToken(sToken);
if (map == null) {
((HttpServletResponse)response).sendRedirect("/web/login");
}
} else {
((HttpServletResponse)response).sendRedirect("/web/login");
}
}
} @Override
public void destroy() { }
}

然后在SpringBoot的Application中增加注解@ServletComponentScan,这样容器会扫描到@Component注解的Filter。

问题出现
出现的问题是:访问的url为/user/*或者/product/*的时候,该过滤器也执行了!
也就是说,WebFilter注解配置的urlPatterns没有起作用。
问题定位:
在查看容器启动日志的时候,发现WebAuthFilter被两次注册,两次映射:

从上图可以看到,WebAuthFilter这个Filter是我们自己定义的,它被做了两次映射,而且两次映射的名字不同(WebAuthFilter和webAuthFilter),分别映射到的URL是“/web/*”和”“/*”。其中WebAuthFilter是我们自己命名的。
这样就解释了为什么所有的URL都会被该Filter处理。

问题定位
WebAuthFilter的第一次映射容易理解,是我们自己通过@WebFilter定义的。
那么webAuthFilter是谁给映射的呢?
必然是Spring容器处理的。
在跟踪源码的时候找到AbstractFilterRegistrationBean抽象类,该类中有一个方法onStartup,应该是容器启动的时候执行的,做的是一些Bean注册的工作。该方法最后调用了configure,在该方法中进行了映射处理。

if (servletNames.isEmpty() && this.urlPatterns.isEmpty()) {
this.logger.info("Mapping filter: '" + registration.getName() + "' to: "
+ Arrays.asList(DEFAULT_URL_MAPPINGS));
registration.addMappingForUrlPatterns(dispatcherTypes, this.matchAfter,
DEFAULT_URL_MAPPINGS);
}
else {
if (!servletNames.isEmpty()) {
this.logger.info("Mapping filter: '" + registration.getName()
+ "' to servlets: " + servletNames);
registration.addMappingForServletNames(dispatcherTypes, this.matchAfter,
servletNames.toArray(new String[servletNames.size()]));
}
if (!this.urlPatterns.isEmpty()) {
this.logger.info("Mapping filter: '" + registration.getName()
+ "' to urls: " + this.urlPatterns);
registration.addMappingForUrlPatterns(dispatcherTypes, this.matchAfter,
this.urlPatterns.toArray(new String[this.urlPatterns.size()]));
}
}

在servletNames和urlPatterns为空的情况下,进行了缺省映射,即映射到“/*”。
置于servletNames和urlPatterns为空的情况,这里没有深究了。
那么,为什么会出现定义的WebAuthFilter被两次注册的情况呢?
仔细分析了一下,认为可能的原因是:@Component和@WebFilter双重注册导致的。

解决办法
解决办法一@WebFilter
在这种情况下,去掉了@Component注解,再次启动服务。查看日志,发现该Filter仅被映射一次,通过浏览器访问相应的url也表现正确。

解决办法二@Component
这种情况下,保留了@Component注解,那么要进行配置的urlPatterns怎么处理呢?
通过FilterRegistrationBean进行@Bean声明,查看源码知道,onStartup进行注册的时候,实际上也是找到了各类RegistrationBean然后分别注册,配置映射。
有各种类型的RegistrationBean:
①AbstractFilterRegistrationBean;
②FilterRegistrationBean;
③ServletListenerRegistrationBean;
④ServletRegistrationBean;
那么我们自然可以通过自声明一个FilterRegistrationBean来进行注册。这种处理方式如下:
去掉FIlter上的@WebFilter注解,增加如下的Configuration类:

@Configuration
public class WebAuthFilterConfig { @Bean
public FilterRegistrationBean webAuthFilterRegistration() {
FilterRegistrationBean registration = new FilterRegistrationBean();
registration.setFilter(webAuthFilter());
registration.setName("WebAuthFilter");
registration.addUrlPatterns("/web/*");
registration.addInitParameter("excludeUrls", "/web/login");
registration.setOrder(0);
return registration;
} @Bean
public Filter webAuthFilter() {
return new WebAuthFilter();
}
}

如此处理,也能达到同样的效果。

经过对比,当然第一种解决方案更直白,更简洁。

后述:网上的很多东西都是带着坑的,直接搬过来用真的有风险!

参考链接:

https://blog.csdn.net/weixin_42114097/article/details/81530628

https://blog.csdn.net/achang07/article/details/79282789

@WebFilter 的使用及采坑的更多相关文章

  1. Cloudera Manager 5.9 和 CDH 5.9 离线安装指南及个人采坑填坑记

    公司的CDH早就装好了,一直想自己装一个玩玩,最近组了台电脑,笔记本就淘汰下来了,加上之前的,一共3台,就在X宝上买了CPU和内存升级了下笔记本,就自己组了个集群. 话说,好想去捡垃圾,捡台8核16线 ...

  2. angular采坑记录

    在angular中会遇到一些莫名的问题,导致不能完成想要的功能,可能是某项用法使用错误,或许是angular相对应不支持,或者是我们功力根本就没有达到.为了在每次采坑之后能有所收获,再遇到时能理解其根 ...

  3. 分布式改造剧集之Redis缓存采坑记

    Redis缓存采坑记 ​ 前言 ​ 这个其实应该属于分布式改造剧集中的一集(第一集见前面博客:http://www.cnblogs.com/Kidezyq/p/8748961.html),本来按照顺序 ...

  4. 采坑:python base64

    需求:  读取文本内容,对字符串进行base64加密 >>> str = 'aaaaaaaaaaaaaaaaaaa\nbbbbbbbbbbbbbbbbbbbbbbbbbbb\nccc ...

  5. Hadoop环境搭建--Docker完全分布式部署Hadoop环境(菜鸟采坑吐血整理)

    系统:Centos 7,内核版本3.10 本文介绍如何从0利用Docker搭建Hadoop环境,制作的镜像文件已经分享,也可以直接使用制作好的镜像文件. 一.宿主机准备工作 0.宿主机(Centos7 ...

  6. Spring Cloud Config采坑记

    1. Spring Cloud Config采坑记 1.1. 问题 在本地运行没问题,本地客户端服务能连上本地服务端服务,可一旦上线,发现本地连不上线上的服务 服务端添加security登录加密,客户 ...

  7. rabbitmq在ios中实战采坑

    1. rabbitmq在ios中实战采坑 1.1. 问题 ios使用rabbitmq连接,没过多久就断开,并报错.且用android做相同的步骤并不会报错,错误如下 Received connecti ...

  8. 从源码看Spring Security之采坑笔记(Spring Boot篇)

    一:唠嗑 鼓捣了两天的Spring Security,踩了不少坑.如果你在学Spring Security,恰好又是使用的Spring Boot,那么给我点个赞吧!这篇博客将会让你了解Spring S ...

  9. Charles 抓包工具安装和采坑记录

    Charles 抓包工具安装和采坑记录 网络抓包是解决网络问题的第一步,也是网络分析的基础.网络出现问题,第一步肯定是通过抓包工具进行路径分析,看哪一步出现异常.做网络爬虫,第一步就是通过抓包工具对目 ...

随机推荐

  1. [Cometoj#4 E]公共子序列_贪心_树状数组_动态规划

    公共子序列 题目链接:https://cometoj.com/contest/39/problem/E?problem_id=1585 数据范围:略. 题解: 首先可以考虑知道了$1$的个数和$3$的 ...

  2. TemplateView , ListView ,DetailView三种常用类视图用法

    参考博客: https://blog.csdn.net/weixin_36571185/article/details/74280102

  3. vim版本更新

    版本问题 ubuntu 14.05 安装完YouCompleteMe后不生效,提示:YouCompleteMe unavailable : requires Vim 7.4.143经过检索与查询,ub ...

  4. Kafka 原理

    消息队列内部实现原理 两种消息传输方式 Kafka kafka 简介 kafka 集群角色 Kafka 工作流程分析 Kafka 生产过程分析 写入方式 分区(partition) 副本(replic ...

  5. Java没有引用传递机制,C#有。

    Java没有引用传递机制,C#有: public class Obj { private Integer myValue; public Integer getMyValue() { return m ...

  6. 编写程序来实现实现strcat()功能

    strcat(字符数组1,字符串2) 字符串2的内容复制连接在字符数组1的后面,其返回值为字符数组1的地址 /* strcat(字符数组1,字符串2) 字符串2的内容复制连接在字符数组1的后面,其返回 ...

  7. Codeforces 1097D. Makoto and a Blackboard

    传送门 首先考虑如果 $n$ 只有一个质因数的情况,即 $n=p^t$ 那么显然可以 $dp$ ,设 $f[i][j]$ 表示第 $i$ 步,当前剩下 $p^j$ 的概率 那么转移很简单: $f[i] ...

  8. 怎样监听xhr.readyState值的变化

    可以使用 xhr.onreadystatechange 属性指向的函数去监听 xhr.readyState 值的变化. 示例如下: var xhr = new XMLHttpRequest(); xh ...

  9. 服务器上office不能正常使用?

    (1)确保dll版本和服务器上office版本一致 (2)配置dcom (3)项目配置文件中添加用户模拟语句 <system.web> <identity impersonate=& ...

  10. AngularJS-02 数据绑定和表达式

    AngularJS----数据绑定和表达式 1.表达式是AngularJS模板引擎的重要内容,也是视图View的必要组成部分,用来将模型动态转换为可视DOM元素或者其内容. 表达式的形式: 1)常量: ...