> 文章列表 > [Java]过滤器(Filter)

[Java]过滤器(Filter)

[Java]过滤器(Filter)

一、什么是过滤器

过滤器是Servlet的高级特性之一,是实现Filter接口的Java类!
过滤器的执行流程:

 

从上面的图我们可以发现,当浏览器发送请求给服务器的时候,先执行过滤器,然后才访问Web的资源。服务器响应Response,从Web资源抵达浏览器之前,也会途径过滤器。

过滤器的用途:过滤一些敏感的字符串【规定不能出现敏感字符串】、避免中文乱码【规定Web资源都使用UTF-8编码】、权限验证【规定只有带Session或Cookie的浏览器,才能访问web资源】等等等。

也就是说:当需要限制用户访问某些资源时、在处理请求时提前处理某些资源、服务器响应的内容对其进行处理再返回、我们就是用过滤器来完成的!

二、过滤器的一般用途

1.解决中文乱码问题

只要在过滤器中指定了编码,可以使全站的Web资源都是使用该编码,并且重用性是非常理想的!

public class CharacterEncodingFilter implements Filter {@Overridepublic void destroy() {// TODO Auto-generated method stub}@Overridepublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)throws IOException, ServletException {request.setCharacterEncoding("utf-8");chain.doFilter(request, response);}@Overridepublic void init(FilterConfig arg0) throws ServletException {// TODO Auto-generated method stub}}

web.xml配置:

<filter><filter-name> CharacterEncodingFilter</filter-name><filter-class>com.entor.filter.CharacterEncodingFilter</filter-class>
</filter><filter-mapping><filter-name> CharacterEncodingFilter</filter-name><url-pattern>/*</url-pattern>
</filter-mapping>

2.过滤器 API

只要Java类实现了Filter接口就可以称为过滤器!Filter接口的方法也十分简单:

其中init()和destory()方法就不用多说了,他俩跟Servlet是一样的。只有在Web服务器加载和销毁的时候被执行,只会被执行一次!

值得注意的是doFilter()方法,它有三个参数(ServletRequest,ServletResponse,FilterChain),从前两个参数我们可以发现:过滤器可以完成任何协议的过滤操作!

FilterChain是一个接口,里面又定义了doFilter()方法。这究竟是怎么回事啊??????

我们可以这样理解:过滤器不单单只有一个,那么我们怎么管理这些过滤器呢?在Java中就使用了链式结构。把所有的过滤器都放在FilterChain里边,如果符合条件,就执行下一个过滤器(如果没有过滤器了,就执行目标资源)。

上面的话好像有点拗口,我们可以想象生活的例子:现在我想在茶杯上能过滤出石头和茶叶出来。石头在一层,茶叶在一层。所以茶杯的过滤装置应该有两层滤网。这个过滤装置就是FilterChain,过滤石头的滤网和过滤茶叶的滤网就是Filter。在石头滤网中,茶叶是属于下一层的,就把茶叶放行,让茶叶的滤网过滤茶叶。过滤完茶叶了,剩下的就是茶(茶就可以比喻成我们的目标资源)

三、快速入门

写一个简单的过滤器
实现Filter接口的Java类就被称作为过滤器

    public class FilterDemo1 implements Filter {public void destroy() {}public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {//执行这一句,说明放行(让下一个过滤器执行,如果没有过滤器了,就执行执行目标资源)chain.doFilter(req, resp);}public void init(FilterConfig config) throws ServletException {}}

1.filter部署

过滤器和Servlet是一样的,需要部署到Web服务器上的。

  • 第一种方式:在web.xml文件中配置
   <filter><filter-name>FilterDemo1</filter-name><filter-class>FilterDemo1</filter-class><init-param><param-name>word_file</param-name> <param-value>/WEB-INF/word.txt</param-value></init-param></filter><filter-mapping><filter-name>FilterDemo1</filter-name><url-pattern>/*</url-pattern></filter-mapping>

<filter>用于注册过滤器

  • <filter-name>用于为过滤器指定一个名字,该元素的内容不能为空。
  • <filter-class>元素用于指定过滤器的完整的限定类名。
  • <init-param>元素用于为过滤器指定初始化参数,它的子元素

<filter-mapping>元素用于设置一个Filter 所负责拦截的资源。

  • <filter-name>子元素用于设置filter的注册名称。该值必须存在
  • <url-pattern>设置 filter 所拦截的请求路径(过滤器关联的URL样式)
  • 第二种方式:通过注解配置
//@Component//无需添加此注解,在启动类添加@ServletComponentScan注解后,会自动将带有@WebFilter的注解进行注入!
@WebFilter(urlPatterns = "/lvjia/carbodyad/api/*", filterName = "rest0PubFilter")
@Order(1)//指定过滤器的执行顺序,值越大越靠后执行
public class Rest0PubFilter implements Filter {@Overridepublic void init(FilterConfig filterConfig) {//初始化过滤器System.out.println("getFilterName:"+filterConfig.getFilterName());//返回<filter-name>元素的设置值。System.out.println("getServletContext:"+filterConfig.getServletContext());//返回FilterConfig对象中所包装的ServletContext对象的引用。System.out.println("getInitParameter:"+filterConfig.getInitParameter("cacheTimeout"));//用于返回在web.xml文件中为Filter所设置的某个名称的初始化的参数值System.out.println("getInitParameterNames:"+filterConfig.getInitParameterNames());//返回一个Enumeration集合对象。}@Overridepublic void doFilter(ServletRequest servletRequest, ServletResponse servletResponse,             FilterChain filterChain) throws IOException, ServletException {if(false){response.sendRedirect("http://localhost:8081/demo/test/login");//重定向}filterChain.doFilter(servletRequest, servletResponse);//doFilter将请求转发给过滤器链下一个filter , 如果没有filter那就是你请求的资源}@Overridepublic void destroy() {}}
@SpringBootApplication
@ServletComponentScan   //Servlet、Filter、Listener 可以直接通过 @WebServlet、@WebFilter、@WebListener 注解自动注册,无需其他代码。
public class Application {public static void main(String[] args) {SpringApplication.run(Application.class, args);}
}

 

@WebFilter常用属性

属性 类型 是否必需 说明
asyncSupported boolean 指定Filter是否支持异步模式
dispatcherTypes DispatcherType[] 指定Filter对哪种方式的请求进行过滤。支持的属性:ASYNC、ERROR、FORWARD、INCLUDE、REQUEST;默认过滤所有方式的请求
filterName String Filter名称
initParams WebInitParam[] 配置参数
displayName String Filter显示名
servletNames String[] 指定对哪些Servlet进行过滤
urlPatterns/value String[] 两个属性作用相同,指定拦截的路径

过滤器的urlPatterns的过滤路径规则:

  • 全路径匹配: /abc/myServlet1.do
  • 部分路径匹配: /abc/*
  • 通配符匹配 :/*
  • 后缀名匹配 :*.do (注意:前面不加/)

2.过滤器的执行顺序

上面已经说过了,过滤器的doFilter()方法是极其重要的,FilterChain接口是代表着所有的Filter,FilterChain中的doFilter()方法决定着是否放行下一个过滤器执行(如果没有过滤器了,就执行目标资源)。

四、Filter简单应用

filter的三种典型应用:

  • 可以在filter中根据条件决定是否调用chain.doFilter(request, response)方法,即是否让目标资源执行
  • 在让目标资源执行之前,可以对request\\response作预处理,再让目标资源执行
  • 在目标资源执行之后,可以捕获目标资源的执行结果,从而实现一些特殊的功能

1.禁止浏览器缓存所有动态页面

    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {//让Web资源不缓存,很简单,设置http中response的请求头即可了!//我们使用的是http协议,ServletResponse并没有能够设置请求头的方法,所以要强转成HttpServletRequest//一般我们写Filter都会把他俩强转成Http类型的HttpServletRequest request = (HttpServletRequest) req;HttpServletResponse response = (HttpServletResponse) resp;response.setDateHeader("Expires", -1);response.setHeader("Cache-Control", "no-cache");response.setHeader("Pragma", "no-cache");//放行目标资源的response已经设置成不缓存的了chain.doFilter(request, response);}
  • 没有过滤之前,响应头是这样的:

     

  • 过滤之后,响应头是这样的:

     

2.实现自动登陆

  • 实体:
 private String username ;private String password;public User() {}public User(String username, String password) {this.username = username;this.password = password;}//各种setter和getter
  • 集合模拟数据库
   public class UserDB {private static List<User> users = new ArrayList<>();static {users.add(new User("aaa", "123"));users.add(new User("bbb", "123"));users.add(new User("ccc", "123"));}public static List<User> getUsers() {return users;}public static void setUsers(List<User> users) {UserDB.users = users;}}
  • 开发dao
 public User find(String username, String password) {List<User> userList = UserDB.getUsers();//遍历List集合,看看有没有对应的username和passwordfor (User user : userList) {if (user.getUsername().equals(username) && user.getPassword().equals(password)) {return user;}}return null;}
  • 登陆界面
<form action="${pageContext.request.contextPath}/LoginServlet">用户名<input type="text" name="username"><br>密码<input type="password" name="password"><br><input type="radio" name="time" value="10">10分钟<input type="radio" name="time" value="30">30分钟<input type="radio" name="time" value="60">1小时<br><input type="submit" value="登陆"></form>
  • 处理登陆的Servlet
    //得到客户端发送过来的数据String username = request.getParameter("username");String password = request.getParameter("password");UserDao userDao = new UserDao();User user = userDao.find(username, password);if (user == null) {request.setAttribute("message", "用户名或密码是错的!");request.getRequestDispatcher("/message.jsp").forward(request, response);}//如果不是为空,那么在session中保存一个属性request.getSession().setAttribute("user", user);request.setAttribute("message", "恭喜你,已经登陆了!");//如果想要用户关闭了浏览器,还能登陆,就必须要用到Cookie技术了Cookie cookie = new Cookie("autoLogin", user.getUsername() + "." + user.getPassword());//设置Cookie的最大声明周期为用户指定的cookie.setMaxAge(Integer.parseInt(request.getParameter("time")) * 60);//把Cookie返回给浏览器response.addCookie(cookie);//跳转到提示页面request.getRequestDispatcher("/message.jsp").forward(request, response);
  • 过滤器
       HttpServletResponse response = (HttpServletResponse) resp;HttpServletRequest request = (HttpServletRequest) req;//如果用户没有关闭浏览器,就不需要Cookie做拼接登陆了if (request.getSession().getAttribute("user") != null) {chain.doFilter(request, response);return;}//用户关闭了浏览器,session的值就获取不到了。所以要通过Cookie来自动登陆Cookie[] cookies = request.getCookies();String value = null;for (int i = 0; cookies != null && i < cookies.length; i++) {if (cookies[i].getName().equals("autoLogin")) {value = cookies[i].getValue();}}//得到Cookie的用户名和密码if (value != null) {String username = value.split("\\\\.")[0];String password = value.split("\\\\.")[1];UserDao userDao = new UserDao();User user = userDao.find(username, password);if (user != null) {request.getSession().setAttribute("user", user);}}chain.doFilter(request, response);
  • 效果:

推币机