JAVA 09 Filter,Listener

Filter

执行任何request,response的操作
在这里插入图片描述
在这里插入图片描述

请求与响应时都会经过Filter

在chain.doFilter方法之前的在请求时执行,在chain.doFilter方法之后在响应时执行

Tomcat启动时就会创建,关闭时销毁

在这里插入图片描述

Filter配置

1.配置web.xml

方式同Servlet

<!--    配置过滤器-->
    <filter>
<!--        filter名称-->
        <filter-name>MyFilter</filter-name>
<!--        全限定名-->
        <filter-class>MyFilter</filter-class>
    </filter>
    <filter-mapping>
<!--        映射名,与filter名称对应-->
        <filter-name>MyFilter</filter-name>
<!--        过滤拦截路径-->
        <url-pattern>/hello.jsp</url-pattern>
    </filter-mapping>
使用注解

路径中 不可使用 /
在这里插入图片描述

过滤器链

多个Filter可拦截同一个网页

请求时先1后2,响应时先2后1
请求时先执行的,响应时会后执行
在这里插入图片描述

拦截优先级

1.如果为web.xml,按照filter-mapping注册顺序,从上往下。谁写在前面谁优先级高
2.使用注解时,是按照类全名称的字符串顺序决定作用顺序。

web.xml配置优先级高于注解配置
如果注解和web.xml同时配置,会创建多个过滤器对象,造成过滤多次。

参数配置

可在初始方法init方法中获得,方法同Servlet

web.xml中配置

<init-param>
      <param-name>namespace</param-name>
      <param-value>chichi</param-value>
</init-param>

注解中配置
方式同Servlet
在这里插入图片描述

大坑大坑:看别人代码时要注意
metadata-complete属性表示部署时当前的配置是否完全,值为true,表示完全,只会应用web.xml的配置,而不会去扫描类似@WebServlet,@WebFilter,@WebListener等注解和web-frame.xml配置。
默认值是metadata-complete=false,即不完全,会对项目中类进行扫描,是否有相关的注解配置,同时也会加载web-frame.xml等插件配置。

Filter应用

正常流程:

客户端第一次请求资源,服务端会把资源发送给服务端,并把ETag和Last-Modified发送给客户端,当客户端再次请求时,会带着这个去请求服务端,如果和服务端的信息没差别,服务端就发送304,告诉客户端上自己的缓存里去找。
在这里插入图片描述

禁止缓存缓存动态页面

通过下面的设置可让游览器不能直接使用缓存,必须要先请求服务器,服务器返给游览器302,游览器再从本地缓存中得到数据,目前的游览器都默认此功能
在这里插入图片描述
Cache-Control:no-cache:指示请求或响应消息不能缓存,实际上是可以存储在本地缓存区中的,只是在与原始服务器进行新鲜度验证之前,缓存不能将其提供给客户端使用。 
通过以上设置可禁止游览器缓存动态页面

Filter设置响应头,直接使用本地缓存

第一次请求响应时,设置响应头,再次请求相同数据时直接从本地缓存返回,不再向服务器提交请求(仅IE游览器有效,Chrome会忽略这个设置)

在doFilter方法中加入

response.setHeader("Cache-Control","max-age:600");
response.addDateHeader("Expires",System.currentTimeMillis()+600000);

max-age 缓存储存的时间 单位时间为s
Expires:缓存过期时间 单位ms

自动登录

第一次访问登录后,将账户密码放在cookie中返回。为首页加入Filter。
下次访问首页时(刷新或再次打开游览器访问),
1.先检查当前用户是否在登录状态(Session中有没有用户信息),若Session存在用户信息,即刷新情况,直接进入首页
2.若不存在,再验证cookie中有正确的账户密码,正确则直接进入首页,并把数据放入Session中,不再需要登录,否则转入登录界面

//        1.判断当前是否处于登录状态
        if (user!=null){
//            已登录,去首页
            chain.doFilter(req,resp);
            return;
        }
        Cookie[] cookies = request.getCookies();
        if (cookies!=null){
            for (Cookie cookie : cookies) {
                if (cookie.getName().equals("userinfo")){
                    String value = cookie.getValue();
                    String[] split = value.split("#");
                    if (split[0].equals("admin")&&split[1].equals("admin")){
                        request.getSession().setAttribute("user",split[0]);
                    }else {
                        // cookie被篡改了,无用了,使其失效
                        Cookie cookie1 = new Cookie("userinfo", "");
                        cookie1.setPath("/0904web1");
                        cookie1.setMaxAge(0);
                        response.addCookie(cookie1);
                    }

                }
            }
        }
        chain.doFilter(req, resp);

脏词过滤

表单输入内容中含有脏词,为表单接收页加入Filter,将它变为**

自定义MyRequest,重写getParameter方法,实现对表单输入内容的改变

在chain.doFilter方法中传入自定义MyRequest,这样在服务器中使用getParameter方法获得表单数据时,得到的就是我们进行过滤之后的内容。

static class MyRequest extends HttpServletRequestWrapper {
        // 脏词集合
        private List<String> dirtyWords;
        private HttpServletRequest request;
        public MyRequest(HttpServletRequest request) {
            super(request);
            this.request = request;
            dirtyWords = new ArrayList<>();
            dirtyWords.add("猪");
            dirtyWords.add("狗");
        }
        @Override
        public String getParameter(String name) {
            String value = super.getParameter(name);
            for (String dirtyWord : dirtyWords) {
                value = value.replaceAll(dirtyWord, "***");
            }
            return value;
        }
    }

使用时将此MyRequest传入chain.doFilter中即可

JSP页面压缩

仅对页面压缩,并不对其中的图片等需再次请求的压缩
JSP是一个文本页面,响应时会固定调用getWriter方法的流写入到response中,若其中有图片等的,服务器会返回一个重定向地址,让游览器再次请求图片,这次使用二进制流,不归我们管。我们仅对response重写时仅需重写getWriter方法
在这里插入图片描述

在Filter中:
doFilter方法前:传给服务器一个自定义MyResponse,重写getWriter方法,让服务器将响应内容写入其中内存流 ByteArrayOutputStream中
doFilter方法后:将服务器传回响应内容通过压缩流GZIPOutputStream压缩至内存流,最后通过响应字节流将内容输出。
此方法能达到约3:1的压缩比

@WebFilter(filterName = "GipFilter",value = "/news.jsp")
public class GipFilter implements Filter {
    public void destroy() {
    }
    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
        MyResponse myResponse = new MyResponse((HttpServletResponse) resp);
        chain.doFilter(req,myResponse);
        ByteArrayOutputStream bos = myResponse.getBos();
        System.out.println("原始大小"+bos.size());
        // 内容通过压缩流压缩后再次写入内存流
        ByteArrayOutputStream newBos = new ByteArrayOutputStream();
        GZIPOutputStream gs =new GZIPOutputStream(newBos);
        gs.write(bos.toByteArray());
        // 之后要记得 flush() 和finish() ,不然压缩包的东西是空的
        gs.flush();;
        gs.close();
        System.out.println("压缩后大小"+newBos.size());
        // 响应给客户端
        // 设置响应头,告知返回内容为压缩的
        myResponse.setHeader("Content-Encoding","gzip");
        myResponse.getOutputStream().write(newBos.toByteArray());
    }

    public void init(FilterConfig config) throws ServletException {

    }
    static class MyResponse extends HttpServletResponseWrapper {
        private PrintWriter pw;
        private HttpServletResponse response;
        // 将服务器响应写入内存流中
        private ByteArrayOutputStream bos;
        public MyResponse(HttpServletResponse response) {
            super(response);
            this.response = response;
            bos = new ByteArrayOutputStream();
            try {
                // 将Printer流中内容写入内存流
                pw = new PrintWriter(new OutputStreamWriter(bos,"utf-8"));
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }
        }

        @Override
        public PrintWriter getWriter() throws IOException {
            return pw;
        }
        public ByteArrayOutputStream getBos(){
            // 刷新,防止缓冲
            if (pw != null) {
                pw.flush();
            }
            return bos;
        }
    }
}

实际图片防盗链

HTTP协议中,如果从一个网页跳到另一个网页,HTTP头字段里面会带个Referer。图片服务器通过检测Referer是否来自规定域名,来进行防盗链。

作用:防止其它网站盗用你的图片。

在这里插入图片描述
只有本网站才能访问的资源(照片),如果别的网站访问这个照片,请求是别的网站的,这就是盗取。
在这里插入图片描述

Listener监听器

在这里插入图片描述

监听器类型

三大类
在这里插入图片描述
感知型要让被监听的对象的类实现接口,监听的是对象,当这个对象被放入session中时,会触发监听器。
感知型监控器不需要注册
在这里插入图片描述
钝化指序列化,内存到硬盘,活化指反序列化硬盘到内存,
对象的类需要同时实现HttpSessionActivationListener接口和Serializable接口
在这里插入图片描述

生命周期监听器应用
application:创建必要初始化
session:通过创建来统计网站的用户访问量

配置方式

在这里插入图片描述

实例

继承接口,实现对应方法

package listener; /**
 * 2020/9/4
 * 21:28
 * zmx
 */

import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.annotation.WebListener;
import javax.servlet.http.HttpSessionAttributeListener;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;
import javax.servlet.http.HttpSessionBindingEvent;

@WebListener()
public class LifeCycleListener implements ServletContextListener,
        HttpSessionListener, HttpSessionAttributeListener {

    // Public constructor is required by servlet spec
    public LifeCycleListener() {
    }

    // -------------------------------------------------------
    // ServletContextListener implementation
    // -------------------------------------------------------
    public void contextInitialized(ServletContextEvent sce) {
      /* This method is called when the servlet context is
         initialized(when the Web application is deployed). 
         You can initialize servlet context related data here.
      */
    }

    public void contextDestroyed(ServletContextEvent sce) {
      /* This method is invoked when the Servlet Context 
         (the Web application) is undeployed or 
         Application Server shuts down.
      */
    }

    // -------------------------------------------------------
    // HttpSessionListener implementation
    // -------------------------------------------------------
    public void sessionCreated(HttpSessionEvent se) {
        /* Session is created. */
    }

    public void sessionDestroyed(HttpSessionEvent se) {
        /* Session is destroyed. */
    }

    // -------------------------------------------------------
    // HttpSessionAttributeListener implementation
    // -------------------------------------------------------

    public void attributeAdded(HttpSessionBindingEvent sbe) {
      /* This method is called when an attribute 
         is added to a session.
      */
    }

    public void attributeRemoved(HttpSessionBindingEvent sbe) {
      /* This method is called when an attribute
         is removed from a session.
      */
    }

    public void attributeReplaced(HttpSessionBindingEvent sbe) {
      /* This method is invoked when an attribute
         is replaced in a session.
      */
    }
}

扩展

自定义的request和response

创建自定义的request和response。分别继承HttpServletRequestWrapper,HttpServletResponseWrapper.(这是两个包装类)
重写其中的某些方法,来达到我们自己的目的,如重写其@Override public String getParameter(String name) 方法
我们可以对表单内容进行先进行操作。当调用自定义request该方法时,就可以获得我们想要的结果。而不是表单传过来的内容。
如利用Filter实现脏词过滤

内存操作流

ByteArrayInputStream和ByteArrayOutputStream

ByteArrayOutputStream

将内容输出到内存中,而不是文件中
其中的数据被写入一个 byte 数组。缓冲区会随着数据的不断写入而自动增长。可使用 toByteArray() 和 toString() 获取数据
内存流无法使用close关闭,不会产生任何 IOException

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值