Java二阶段 Filter and Listener

tech2025-08-09  3

Filter(过滤器)也是在服务器,负责检查过滤,也可以执行任何request,response的操作。

过滤器是在服务器开启时,就已经创建了。而servlet是可以改变创建时机的。,能过滤服务器的一切资源。切记,能执行request,response的一切操作和修改request,response的操作。还能转向。

配置Filter(web.xml)

在web.xml中

<filter> <filter-name>MyFilter</filter-name> <filter-class>com.qf.servlet.MyFilter</filter-class> </filter> //决定了拦截的顺序。先写的那个mapping,那个mapping就先执行 <filter-mapping> <filter-name>MyFilter</filter-name> <!-- 拦截的路径--> <url-pattern>/hello.jsp</url-pattern> </filter-mapping>

Filter在服务器启动时,Filter就创建好了。执行顺寻与创建顺序无关。

注解(@webfilter)

在web.xml的顶部需要配置 metadata-complete=“false”,默认为false,如果改为true,不会去扫描注解,只会查看配置文件 采用注解的形式,过滤器的执行顺序与全限定名有关。会根据全限定名进行比较。

过滤器链

Filter的优先级

过滤器配置参数

web.xml

<filter> <filter-name>MyFilter</filter-name> <filter-class>com.qf.servlet.MyFilter</filter-class> <init-param> <param-name>username</param-name> <param-value>xiaoxuan</param-value> </init-param> </filter>

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

过滤器的应用

浏览器缓存

cache-cotrol:缓存存活时间,单位是秒,如果没有缓存,no-cache expires:过期时间,单位是毫秒,没有缓存,0 pargma:控制缓存,没有缓存 nocache 现在的大部分浏览器不会缓存动态页面,都会去请求服务端。

禁止缓存

现在浏览器已经没有问题了。

HttpServletResponse response = (HttpServletResponse)resp; //设置无缓存 response.setHeader("cache-control", "no-cache"); //设置过期时间 response.setDateHeader("expires", 0); response.setHeader("pragma", "no-cache"); chain.doFilter(req, resp);

允许缓存

HttpServletResponse response = (HttpServletResponse)resp; //单位为秒,缓存的时间 response.setHeader("cache-control", "max-age=600"); //单位毫秒,记录过期的时间 response.setDateHeader("expires", System.currentTimeMillis()+600000); chain.doFilter(req, resp);

这样就可以不需要访问服务器,浏览器自己去找自己的缓存。

自动登录

当成功登录之后,应该把自己的用户名和密码保存起来,下次能直接访问数据。 总的来说,就是在登录成功,检查是否选择自动登录按钮,如果选择了,就把账号和密码通过加密,存放到cookie,设置有效时间,当下次进入项目时,先检查当前用户是否在登录,如果没有,就把cookie中的数据解密,验证账号和密码是否正确,如果正确,就把数据存放到session中。

UserService userService = new UserServiceImpl(); HttpServletRequest request = (HttpServletRequest)req; HttpServletResponse response = (HttpServletResponse)resp; //判断当前是否登录 String username = (String) request.getSession().getAttribute("username"); if(username != null) { chain.doFilter(req,resp); return; } //获取cookie Cookie[] cookies = request.getCookies(); if(cookies != null){ for (Cookie cookie : cookies) { if(cookie.getName().equals("userInfo")){ String value = cookie.getValue(); //解密 byte[] decode = Base64.getDecoder().decode(value); String info = new String(decode); String[] infos = info.split("#"); User user = userService.queryByUserNameAndPassword(infos[0], infos[1]); if(user != null) { request.getSession().setAttribute("username", infos[0]); }else { Cookie cookie1 = new Cookie(infos[0],""); cookie1.setPath(request.getContextPath()); cookie1.setMaxAge(0); response.addCookie(cookie1); } } } } chain.doFilter(req, resp);

过滤敏感词汇

一般我们都是将数据发送给服务器,服务器在响应回来,我们只要知道服务器用的什么方法,只要在过滤器中重载某些方法,并把自己创建的requset或者response传给目标资源,目标资源就调用的我们的重载的方法,依次达到不同的效果。 比如我们将评论提交给服务器,服务器需要把敏感词进行过滤。 这就是目标资源,它通过使用request.getParameter()方法得到数据。所以我们就在过滤器中把这个方法进行重载

@WebServlet(name = "CommentServlet", value = "/comment") public class CommentServlet extends HttpServlet { protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.setCharacterEncoding("utf-8"); response.setContentType("text/html;charset=utf-8"); String comment = request.getParameter("comment"); response.getWriter().write(comment); }

过滤器:

@WebFilter(filterName = "CleanDirtyFilter", value = "/comment") public class CleanDirtyFilter implements Filter { public void destroy() { } public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException { HttpServletRequest realRequest = (HttpServletRequest)req; //把req中的getParameter()方法进行重载,把自己创建的request传给下一个链或者目标资源 MyRequest request = new MyRequest(realRequest); chain.doFilter(request, resp); } public void init(FilterConfig config) throws ServletException { } //HttpServletRequestWrapper只是包装了request,里面存了request的引用,里面的方法都是和request相同的,方法里面是调用的request的方法 class MyRequest extends HttpServletRequestWrapper { //需要把脏词保存起来 private List<String> dirtyWords; public MyRequest(HttpServletRequest request) { super(request); dirtyWords = new ArrayList<>(); dirtyWords.add("笨猪"); dirtyWords.add("好兄弟"); dirtyWords.add("好朋友"); dirtyWords.add("铁子"); } @Override public String getParameter(String name) { String comment = super.getParameter(name); for (String dirtyWord : dirtyWords) { comment = comment.replaceAll(dirtyWord, "***"); } return comment; } } }

结果:

压缩

一般都是压缩文本,比如我们去访问一个新闻 显示为4669字节,数据太大,影响效率。所以我们需要进行压缩。 我们是用jsp显示的这条内容。

@WebFilter(filterName = "NewsFilter",value = "/news.jsp") public class NewsFilter implements Filter { public void destroy() { } public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException { //修改resp HttpServletResponse realResponse = (HttpServletResponse)resp; MyResponse response = new MyResponse(realResponse); chain.doFilter(req, response); //得到内存流 ByteArrayOutputStream byteOut = response.getByteOut(); //因为内存输出流的toByteArray(),toString就可以把缓冲区的数据给读出来,这个和普通的输出流是不一样的。 //创建压缩流 ByteArrayOutputStream newByteOut = new ByteArrayOutputStream(); System.out.println("压缩之前的大小:"+byteOut.size()); GZIPOutputStream gzip = new GZIPOutputStream(newByteOut); gzip.write(byteOut.toByteArray()); gzip.finish(); gzip.close(); //告诉浏览器改内容已经被压缩 response.setHeader("Content-Encoding", "gzip"); System.out.println("压缩之后的内容:"+newByteOut.size()); response.getOutputStream().write(newByteOut.toByteArray() ); } public void init(FilterConfig config) throws ServletException { } static class MyResponse extends HttpServletResponseWrapper{ //打印流 private PrintWriter pw; //内存输出操作流 private ByteArrayOutputStream byteOut; public MyResponse(HttpServletResponse response) { super(response); try { byteOut = new ByteArrayOutputStream(); //将打印流写入内存 pw = new PrintWriter(new OutputStreamWriter(byteOut,"utf-8")); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } } //重载方法,让目标资源用我创建的打印流 @Override public PrintWriter getWriter() throws IOException { //返回的是我创建的流 return pw; } //等响应回来我们还需要从内存流里读出数据。所以我们还需要获取内存流 public ByteArrayOutputStream getByteOut(){ //不为空,需要刷新缓冲区 if(pw != null) { pw.flush(); } return byteOut; } } }

实现图片防盗链

只有本网站才能访问的资源(照片),如果别的网站访问这个照片,请求是别的网站的,这就是 盗取。这个也可以通过过滤器来过滤。

@WebFilter(filterName = "StealFilter", value = "*.png") public class StealFilter implements Filter { public void destroy() { } public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException { HttpServletRequest request = (HttpServletRequest)req; //请求服务器的客户端的地址 String referer = request.getHeader("referer"); System.out.println(referer); //图片的地址 String path = "http://localhost:8080"+request.getContextPath(); //refer何时为空,在浏览器上面写上该地址去访问,refer就为null if(referer != null && referer.startsWith(path)){ HttpServletResponse response = (HttpServletResponse)resp; //相同说明是正常访问 chain.doFilter(req, resp); return; } //如果是非正常访问 request.getRequestDispatcher("/upload/steal.jpg").forward(request,resp); System.out.println(request.getRequestURI()); chain.doFilter(req,resp); } public void init(FilterConfig config) throws ServletException { } }

监听器

监听器的类型

监听某个的对象放入session中或者移出

是对象要实现这个接口 监听的是这个对象 不需要注解或者配置web.xml文件

public class User implements HttpSessionBindingListener { private String name; private String color; public User() { } public User(String name, String color) { this.name = name; this.color = color; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getColor() { return color; } public void setColor(String color) { this.color = color; } @Override public String toString() { return "User{" + "name='" + name + '\'' + ", color='" + color + '\'' + '}'; } @Override public void valueBound(HttpSessionBindingEvent event) { System.out.println("对象进入了session"); } @Override public void valueUnbound(HttpSessionBindingEvent event) { System.out.println("对象移出了session"); } } <% User user = new User("小红","绿色"); session.setAttribute("user", user); %> <body> ${pageContext.session.removeAttribute("user")} </body>

对象的钝化与活化

钝化:内存到硬盘 活化:硬盘到内存 还需要实现序列化接口

配置文件:

管理session

因为在服务器中创建的session默认是30分钟清除,也就是失效,但是我们可以自己设置有效时间。这就用到了监听器。

@WebListener public class SessionManager implements ServletContextListener, HttpSessionListener { //容器,存放session private ConcurrentHashMap<String, HttpSession/> map; //定时器 private Timer timer; @Override public void contextInitialized(ServletContextEvent sce) { //初始化 map = new ConcurrentHashMap<>(); timer = new Timer(); //定时器开始 timer.schedule(new TimerTask() { @Override public void run() { if(map.size()>0){ for (Map.Entry<String, HttpSession> entry : map.entrySet()) { HttpSession value = entry.getValue(); //比较时间 if(System.currentTimeMillis()-value.getLastAccessedTime()>60000) { //移出 map.remove(entry.getValue()); System.out.println("过期了"+value.hashCode()); //失效 value.invalidate(); } } } } },0,10000); } @Override public void contextDestroyed(ServletContextEvent sce) { map = null; timer.cancel(); } @Override public void sessionCreated(HttpSessionEvent se) { System.out.println("创建了session"+se.getSource().hashCode()); HttpSession session = (HttpSession)se.getSource(); map.put(session.getId(),session); } @Override public void sessionDestroyed(HttpSessionEvent se) { System.out.println("session销毁了"); } }
最新回复(0)