美文网首页
Java 过滤器(Filter)

Java 过滤器(Filter)

作者: 超天大圣JR | 来源:发表于2019-12-23 15:31 被阅读0次

    一、什么是过滤器

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

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

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

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

    二、过滤器的一般用途

    1.解决中文乱码问题

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

    public class CharacterEncodingFilter implements Filter {
    
        @Override
        public void destroy() {
            // TODO Auto-generated method stub
    
        }
    
        @Override
        public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
                throws IOException, ServletException {
            request.setCharacterEncoding("utf-8");
            chain.doFilter(request, response);
    
        }
    
        @Override
        public 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 {
     
     
        @Override
        public 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集合对象。
        }
     
        @Override
        public 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那就是你请求的资源
     
     }
     
        @Override
        public 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和password
            for (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);
    
    • 效果:

    相关文章

      网友评论

          本文标题:Java 过滤器(Filter)

          本文链接:https://www.haomeiwen.com/subject/nqsznctx.html