JavaWeb 后端 <十二> 之 过滤器 filter 乱码、不缓存、脏话、标记、自动登录、全站压缩过滤器
1. 过滤器是什么?有什么?
1、过滤器属于Servlet规范,从2.3版本就开始有了。
2、过滤器就是对访问的内容进行筛选(拦截)。利用过滤器对请求和响应进行过滤
2. 编写步骤和执行过程
1、编码步骤:
a、编写一个类:实现javax.servlet.Filter接口
public class FilterDemo1 implements Filter { public FilterDemo1(){ System.out.println("调用了默认的构造方法"); } //用户每次访问被过滤的资源,都会由服务器调用该方法实现过滤 public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { System.out.println("FilterDemo1执行过滤前"); //对请求进行拦截,代码写在这 chain.doFilter(request, response);//放行 //对响应进行拦截,代码写在这 System.out.println("FilterDemo1执行过滤后"); } public void destroy() { System.out.println("调用了销毁方法"); } public void init(FilterConfig filterConfig) throws ServletException { System.out.println("调用初始化方法"); } }
b、配置web.xml,指定需要过滤的资源。(和Servlet的配置相当类似)
2、过滤器的执行过程(生命周期)
生命周期:
诞生:过滤器的实例是在应用被加载时就完成的实例化,并初始化的。
存活:和应用的生命周期一致的。在内存中是单例的。针对拦截范围内的资源访问,每次访问都会调用void doFIlter(request,response.chain)进行拦截。
死亡:应用被卸载。
3. 串联过滤器
一个过滤器接着另外一个过滤器。执行的顺序 按照web.xml的先后顺序
随意 访问 会先直接FilterDemo2 的 再执行 FilterDemo3的
会输出 :
FilterDemo2前
FilterDemo3前
执行内容
FilterDemo3后
FilterDemo2后
4. 案例:
1、解决请求参数(POST)和响应输出的乱码过滤器
//解决post方式请求参数和响应编码问题的过滤器 public class SetCharacterEncodingFilter implements Filter { private FilterConfig filterConfig; public void init(FilterConfig filterConfig) throws ServletException { this.filterConfig = filterConfig; } public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { String encoding = filterConfig.getInitParameter("encoding");//用户可能忘记了配置该参数 if(encoding==null){ encoding = "UTF-8";//默认编码 } request.setCharacterEncoding(encoding);//只能解决POST请求参数的中文问题 response.setCharacterEncoding(encoding);//输出流编码 response.setContentType("text/html;charset="+encoding);//输出流编码,通知了客户端应该使用的编码 chain.doFilter(request, response); } public void destroy() { } }
将 编码类型写在 Filter参数中
2、动态资源不要缓存的过滤器
Servlet/JSP:动态资源不要缓存。
这里 将 Servletrequest 和 ServletResponse 转换为 HttpServlet 的方法 避免发生错误
HttpServletRequest request = null; HttpServletResponse response = null; try{ request = (HttpServletRequest)req; response = (HttpServletResponse)resp;
}catch(Exception e){ throw new RuntimeException("not-http request or response"); }
//控制动态资源不要缓存过滤器 public class NoCacheFilter implements Filter { public void init(FilterConfig filterConfig) throws ServletException { } public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException { // HttpServletRequest request = (HttpServletRequest)req; // HttpServletResponse response = (HttpServletResponse)resp; 不使用该方法 HttpServletRequest request = null; HttpServletResponse response = null; try{ request = (HttpServletRequest)req; response = (HttpServletResponse)resp; }catch(Exception e){ throw new RuntimeException("not-http request or response"); } response.setHeader("Expires", "-1"); response.setHeader("Cache-Control", "no-cache"); response.setHeader("Pragma", "no-cache"); chain.doFilter(request, response); } public void destroy() { } }
动态过滤掉 Servlet 和Jsp
3、静态资源控制缓存时间的过滤器
//控制静态资源的缓存时间 public class StaticResourcesNeedCacheFilter implements Filter { private FilterConfig filterConfig; public void init(FilterConfig filterConfig) throws ServletException { this.filterConfig = filterConfig; } public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException { HttpServletRequest request = null; HttpServletResponse response = null; try{ request = (HttpServletRequest)req; response = (HttpServletResponse)resp; }catch(Exception e){ throw new RuntimeException("not-http request or response"); } long time = 0;//缓存的时间 //根据用户请求的uri地址的后缀:/day19_00_filter/1.html String uri = request.getRequestURI(); String exName = uri.substring(uri.lastIndexOf(".")+1); if("html".equals(exName)){ String value = filterConfig.getInitParameter("html");//小时 time = Long.parseLong(value)*60*60*1000; } if("css".equals(exName)){ String value = filterConfig.getInitParameter("css");//小时 time = Long.parseLong(value)*60*60*1000; } if("js".equals(exName)){ String value = filterConfig.getInitParameter("js");//小时 time = Long.parseLong(value)*60*60*1000; } response.setDateHeader("Expires", System.currentTimeMillis()+time); //Expires 控制时间 chain.doFilter(request, response); } public void destroy() { } }
4、//*用户自动登录过滤器:
使用了Md5 加密
Base64编码:很重要
编写
//自动登录过滤器 public class AutoLoginFilter implements Filter { private BusinessService s = new BusinessServiceImpl(); public void init(FilterConfig filterConfig) throws ServletException { } public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException { HttpServletRequest request = null; HttpServletResponse response = null; try{ request = (HttpServletRequest)req; response = (HttpServletResponse)resp; }catch(Exception e){ throw new RuntimeException("not-http request or response"); } HttpSession session = request.getSession(); //判断用户有没有登录:只管没有登录的 User sUser = (User)session.getAttribute("user"); if(sUser==null){ //找loginInfo的cookie:只管找到的 Cookie cs[] = request.getCookies(); for(int i=0;cs!=null&&i<cs.length;i++){ if("loginInfo".equals(cs[i].getName())){ //解出用户名(Base64)和密码(MD5) String usernamePassword = cs[i].getValue(); String username = usernamePassword.split("_")[0];//base64编码后的 String password = usernamePassword.split("_")[1];//MD5加密后的 //调用Service再次验证正确性 User user = s.login(SecurityUtil.base64decode(username), password); //通过:登录。在HttpSession中设置登录标记 if(user!=null){ session.setAttribute("user", user); } } } } chain.doFilter(request, response); } public void destroy() { } }
5. 过滤器配置的细节
6. 巩固装饰设计模式
一、装饰
1、编写一个类,实现与被包装类(数据库驱动对Connection的实现)相同的接口。(使这个类和数据库的驱动实现有着相同的行为)
2、定义一个变量,引用被包装类的实例。
3、定义构造方法,传入被包装类的实例。
4、对于要改写的方法,编写自己的代码即可。
5、对于不需要改写的方法,调用原有对象的对应方法。
二、装饰变体(BufferedReader本身就是包装类,对Reader的包装。LineNumberReader,对BufferedReader的包装,还是他的子类)
1、编写一个类,继承已经是包装类的类。
2、定义一个变量,引用被包装类的实例。
3、定义构造方法,传入被包装类的实例。
4、覆盖掉需要改写的方法
7. 案例:
1、解决全站中文乱码过滤器
之前解决了 post 的乱码 问题 这里 添加 get的 解决问题
1.定义一个类 EncodingHttpServletRequest 继承 HttpServletRequestWrapper 前对 后 的 包装 得到自己想要的 方法
2.重写 HttpServletRequestWrapper 的 getParameter 方法 。
3.将get传入的值使用 该 super.getCharacterEncoding()的编码 方式返回
如果 使用 get方法 就可以 得到 应有的编码方式
public class SetCharacterEncodingFilter implements Filter { private FilterConfig filterConfig; public void init(FilterConfig filterConfig) throws ServletException { this.filterConfig = filterConfig; } public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException { HttpServletRequest request; HttpServletResponse response; try{ request = (HttpServletRequest)req; response = (HttpServletResponse)resp; }catch(Exception e){ throw new RuntimeException("non-http request or response"); } String encoding = filterConfig.getInitParameter("encoding");//用户可能忘记了配置该参数 if(encoding==null){ encoding = "UTF-8";//默认编码 } request.setCharacterEncoding(encoding);//只能解决POST请求参数的中文问题 response.setCharacterEncoding(encoding);//输出流编码 response.setContentType("text/html;charset="+encoding);//输出流编码,通知了客户端应该使用的编码 EncodingHttpServletRequest erequest = new EncodingHttpServletRequest(request); chain.doFilter(erequest, response); } public void destroy() { } } class EncodingHttpServletRequest extends HttpServletRequestWrapper{ public EncodingHttpServletRequest(HttpServletRequest request){ super(request); } public String getParameter(String name) { String value = super.getParameter(name); if(value==null) return value; //只管get方式 if("get".equalsIgnoreCase(super.getMethod())){ try { value = new String(value.getBytes("ISO-8859-1"),super.getCharacterEncoding()); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } } return value; } }
2、过滤脏话过滤器
1.新建 DWHttpServletRequest 类 继承 HttpServletRequestWrapper ,
2.包装 getParameter 发方法 过滤 脏话
3.执行 内容 时 得到的是 DWHttpServletRequest 的 request 所以使用的方法是 DWHttpServletRequest的 getParameter。
public class DirtyWordsFilter implements Filter { public void init(FilterConfig filterConfig) throws ServletException { } public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException { HttpServletRequest request; HttpServletResponse response; try{ request = (HttpServletRequest)req; response = (HttpServletResponse)resp; }catch(Exception e){ throw new RuntimeException("non-http request or response"); } DWHttpServletRequest dwrequest = new DWHttpServletRequest(request); chain.doFilter(dwrequest, response); } public void destroy() { } } class DWHttpServletRequest extends HttpServletRequestWrapper{ private String[] strs = {"禽兽","畜生","傻B","张新鹏"}; public DWHttpServletRequest(HttpServletRequest request){ super(request); } public String getParameter(String name) { String value = super.getParameter(name); if(value==null) return value; for(String s:strs){ value = value.replace(s, "**"); } return value; } }
3、html标记过滤器
方法和前2种类似
public class HtmlFilter implements Filter { public void init(FilterConfig filterConfig) throws ServletException { } public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException { HttpServletRequest request; HttpServletResponse response; try{ request = (HttpServletRequest)req; response = (HttpServletResponse)resp; }catch(Exception e){ throw new RuntimeException("non-http request or response"); } HtmlHttpServletRequest hrequest = new HtmlHttpServletRequest(request); chain.doFilter(hrequest, response); } public void destroy() { } } class HtmlHttpServletRequest extends HttpServletRequestWrapper{ public HtmlHttpServletRequest(HttpServletRequest request){ super(request); } public String getParameter(String name) { String value = super.getParameter(name); if(value==null) return value; value = htmlFilter(value);//转义字符 return value; } private String htmlFilter(String message) { if (message == null) return (null); char content[] = new char[message.length()]; message.getChars(0, message.length(), content, 0); StringBuffer result = new StringBuffer(content.length + 50); for (int i = 0; i < content.length; i++) { switch (content[i]) { case '<': result.append("<"); break; case '>': result.append(">"); break; case '&': result.append("&"); break; case '"': result.append("""); break; default: result.append(content[i]); } } return (result.toString()); } }
4、//*全站压缩过滤器(有难度)
public class GzipFilter implements Filter { public void init(FilterConfig filterConfig) throws ServletException { } public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException { HttpServletRequest request; HttpServletResponse response; try{ request = (HttpServletRequest)req; response = (HttpServletResponse)resp; }catch(Exception e){ throw new RuntimeException("non-http request or response"); } GzipHttpServletResponse gresponse = new GzipHttpServletResponse(response); chain.doFilter(request, gresponse); //目标资源执行完毕后执行:要压缩 byte b[] = gresponse.getBytes();//得到原始的数据为编码的关键点 System.out.println("压缩前大小:"+b.length); //判断客户是否支持gzip压缩 String acceptEncoding = request.getHeader("Accept-Encoding"); if(acceptEncoding!=null&&acceptEncoding.contains("gzip")){ //支持 ByteArrayOutputStream out = new ByteArrayOutputStream(); GZIPOutputStream gout = new GZIPOutputStream(out); gout.write(b); gout.close(); b = out.toByteArray();//压缩后的数据 System.out.println("压缩后大小:"+b.length); //告知浏览器压缩方式 response.setHeader("Content-Encoding", "gzip"); response.setContentLength(b.length);//告知客户端,正文的长度 } response.getOutputStream().write(b); } public void destroy() { } } class GzipHttpServletResponse extends HttpServletResponseWrapper{ private ByteArrayOutputStream baos = new ByteArrayOutputStream();//存放截获的数据 private PrintWriter pw = null; public GzipHttpServletResponse(HttpServletResponse response){ super(response); } //截获输出的数据:放到baos中 public ServletOutputStream getOutputStream() throws IOException { return new MyServletOutputStream(baos); } //字符流:截获放到baos中 public PrintWriter getWriter() throws IOException { pw = new PrintWriter(new OutputStreamWriter(baos, super.getCharacterEncoding())); return pw; } //获取截获的数据 public byte[] getBytes(){ try { if(pw!=null){ pw.close(); } baos.flush(); } catch (IOException e) { e.printStackTrace(); } return baos.toByteArray(); } } class MyServletOutputStream extends ServletOutputStream{ private ByteArrayOutputStream baos; public MyServletOutputStream(ByteArrayOutputStream baos){ this.baos = baos; } public void write(int b) throws IOException { baos.write(b); } }