java web filter 基础
来源:互联网 发布:天书奇谈网页游戏源码 编辑:程序博客网 时间:2024/05/16 06:36
本文主要对Filter的使用进行讲解。
1、Filter是什么?
Filter 技术是servlet 2.3 新增加的功能。Filter翻译过来的意思就是过滤器,能对客户端的消息进行预处理,然后将请求转发给其它的web组件,可以对ServletHttpRequest和ServletHttpResponse进行修改和检查。例如:在Filter中可以检查用户是否登录,对未登录的用户跳转到登陆界面。
2、过滤器快速入门
要定义一个过滤器,则需要实现javax.servlet.Filter接口,一个过滤器中包含了三个与生命周期相关的方法:
- void init(FilterConfig config) 过滤器初始化时执行,FilterConfig 可以用来获取过滤器的初始化参数。
- void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
- destroy() web容器(tomcat)停止时执行
第一步:创建DemoFilter.java
package cn.zq.filter;import java.io.IOException;import javax.servlet.Filter;import javax.servlet.FilterChain;import javax.servlet.FilterConfig;import javax.servlet.ServletException;import javax.servlet.ServletRequest;import javax.servlet.ServletResponse;public class DemoServlet implements Filter{public void init(FilterConfig config) throws ServletException {System.out.println("DemoServlet.init...");}public void doFilter(ServletRequest req, ServletResponse resp,FilterChain chain) throws IOException, ServletException {System.out.println("DemoServlet.doFilter..."); System.out.println("this = " + this); }public void destroy() {System.out.println("DemoServlet.destroy...");}}
第二步:在web.xml文件中添加如下配置:
<filter> <filter-name>demo</filter-name> <filter-class>cn.zq.filter.DemoServlet</filter-class> </filter> <filter-mapping> <filter-name>demo</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
url-pattern配置为/*,表示过滤所有请求。
启动tomcat,可以看到如下输出:
说明Filter的init方法在web容器启动的时候执行,读者可以自行验证destroy()方法会在web容器停止时执行,访问主页:
在访问主页的时候浏览器是一片空白的,控制台输出上面的消息,Filter只会实例化一次,为什么我们访问不到我们要访问的内容呢?只需要在doFilter方法中加入如下的语句就可以了:
chain.doFilter(req, resp);FilterChain(过滤器链)是用来干什么的呢?这个对象只包含一个void doFilter(ServletRequest request, ServletResponse response)方法,Filter调用此方法去调用下一个web组件(Filter,Servlet等),如果不调用此方法,那么下一个web组件不会被执行。
再放行之前,我们可以在Filter中设置响应头信息,如下:
resp.setContentType("text/html;charset=UTF-8"); chain.doFilter(req, resp);过滤器的过滤过程如下:
过滤器中的各项配置:
- 配置初始化参数:
<filter> <filter-name>demo</filter-name> <filter-class>cn.zq.filter.DemoServlet</filter-class> <init-param> <param-name>encoding</param-name> <param-value>UTF-8</param-value> </init-param> <init-param> <param-name>name</param-name> <param-value>RiccioZhang</param-value> </init-param> </filter> <filter-mapping> <filter-name>demo</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
然后在init方法中获取:
public void init(FilterConfig config) throws ServletException {System.out.println("DemoServlet.init...");String encoding = config.getInitParameter("encoding");String name = config.getInitParameter("name");System.out.println("encoding="+encoding);System.out.println("name="+name);}
<filter> <!-- filter的名字 --> <filter-name>demo</filter-name> <!-- 类名 --> <filter-class>cn.zq.filter.DemoServlet</filter-class> </filter> <!-- 可以有多个 --> <filter-mapping> <!-- 对哪个filter进行配置 --> <filter-name>demo</filter-name> <!-- 配置过滤的url,不能是/ 其他与servlet配置类似 --> <url-pattern>/*</url-pattern> <!-- 根据名字配置对哪个servlet进行过滤 --> <servlet-name>DemoServlet</servlet-name> <!-- ERROR: <error-page>过来的请求 FORWARD: 对转发过来的请求进行过滤,也就是对request.getRequestDispatcher(path).forward(request, response) INCLUDE:对request.getRequestDispatcher(path).include(request, response)过来的请求进行过滤 REQUEST(默认): 对客户端的请求进行拦截 可以配置多个 --> <dispatcher>ERROR</dispatcher> </filter-mapping>
3、Filter的应用
为了便于编写Filter,本节的所有应用中提供了一个通过的Filter的实现:
package cn.zq.filter;import java.io.IOException;import java.io.Serializable;import java.util.Enumeration;import javax.servlet.Filter;import javax.servlet.FilterChain;import javax.servlet.FilterConfig;import javax.servlet.ServletContext;import javax.servlet.ServletException;import javax.servlet.ServletRequest;import javax.servlet.ServletResponse;/** * 通用的Filter的实现 * @author zq * */public abstract class GenericFilter implements Filter,FilterConfig, Serializable{private static final long serialVersionUID = 5497978960987185665L;private FilterConfig filterConfig;/** * 需要初始化,应该覆盖整个方法 */public void init(){}public void init(FilterConfig filterConfig) throws ServletException {this.filterConfig = filterConfig;init();}abstract public void doFilter(ServletRequest request, ServletResponse response,FilterChain chain) throws IOException, ServletException;public void destroy() {}public String getFilterName() {return getFilterConfig().getFilterName();}public String getInitParameter(String name) {return getFilterConfig().getInitParameter(name);}public Enumeration<String> getInitParameterNames() {return getFilterConfig().getInitParameterNames();}public ServletContext getServletContext() {return getFilterConfig().getServletContext();}public FilterConfig getFilterConfig() {return filterConfig;}}
package cn.zq.filter;import java.io.IOException;import javax.servlet.FilterChain;import javax.servlet.ServletException;import javax.servlet.ServletRequest;import javax.servlet.ServletResponse;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;public abstract class HttpFilter extends GenericFilter{private static final long serialVersionUID = 1029993995265394412L;public void doFilter(ServletRequest request, ServletResponse response,FilterChain chain) throws IOException, ServletException {HttpServletRequest req = (HttpServletRequest) request;HttpServletResponse resp = (HttpServletResponse) response;doFilter(req, resp, chain);}protected abstract void doFilter(HttpServletRequest request, HttpServletResponse response,FilterChain chain) throws IOException, ServletException;}
3.1、解决GET和POST获取参数的乱码问题
第一步:创建Filter
package cn.zq.filter;import java.io.IOException;import java.io.UnsupportedEncodingException;import java.util.Iterator;import java.util.Map;import javax.servlet.FilterChain;import javax.servlet.ServletException;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletRequestWrapper;import javax.servlet.http.HttpServletResponse;/** * 处理字符编码的Filter * @author zq * */public class CharacterEncodingFilter extends HttpFilter{private static final long serialVersionUID = -4329981031091311164L;private String characterEncoding = "UTF-8";public void init() {String ce = getInitParameter("characterEncoding");if(ce != null && !ce.equals("")){characterEncoding = ce.toUpperCase();}}public void doFilter(HttpServletRequest request,HttpServletResponse response, FilterChain chain)throws IOException, ServletException {request.setCharacterEncoding(characterEncoding);response.setContentType("text/html;charset="+characterEncoding);//优化:只对GET请求的request进行包装if(request.getMethod().equals("GET")){request = new ParameterHandlerRequest(request);}chain.doFilter(request, response);}private class ParameterHandlerRequest extends HttpServletRequestWrapper{public ParameterHandlerRequest(HttpServletRequest request) {super(request);}public String getParameter(String name) {String value = super.getParameter(name);return getString(value);}private String getString(String value){if(value != null){try {value = new String( value.getBytes("ISO-8859-1"), getRequest().getCharacterEncoding() );} catch (UnsupportedEncodingException e) {e.printStackTrace();}}return value;}public String[] getParameterValues(String name) {String[] values = super.getParameterValues(name);if(null != values){for(int i = 0; i < values.length; i++){values[i] = getString(values[i]);}}return values;}public Map<String, String[]> getParameterMap() {Map<String, String[]> paramMap = super.getParameterMap();Iterator<String[]> it = paramMap.values().iterator();while(it.hasNext()){String[] values = it.next();if(null != values){for(int i = 0; i < values.length; i++){values[i] = getString(values[i]);}}}return paramMap;}}}
第二步:编写配置文件
<filter> <filter-name>CharacterEncodingFilter</filter-name> <filter-class>cn.zq.filter.CharacterEncodingFilter</filter-class> </filter> <filter-mapping> <filter-name>CharacterEncodingFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
这个过滤器应该配置在所有过滤器的前面
第三步:测试
package cn.zq.servlet;import java.io.IOException;import java.util.Iterator;import java.util.Map;import javax.servlet.ServletException;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;public class DemoServlet extends HttpServlet { private static final long serialVersionUID = -4363281555738840730L; public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doPost(request, response); } public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { System.out.println("--------"+request.getMethod()+"--------"); System.out.println("request = " + request); System.out.println("name = " + request.getParameter("name")); String[] names = request.getParameterValues("name"); if(names != null && names.length > 0){ System.out.println("names[0] = " + names[0]); } Map<String, String[]> parameterMap = request.getParameterMap(); for(Iterator<String> it = parameterMap.keySet().iterator(); it.hasNext();){ String key = it.next(); String[] values = parameterMap.get(key); System.out.println(key+"="+values[0] ); } }}
<servlet> <servlet-name>DemoServlet</servlet-name> <servlet-class>cn.zq.servlet.DemoServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>DemoServlet</servlet-name> <url-pattern>/servlet/DemoServlet</url-pattern> </servlet-mapping>
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%><%String path = request.getContextPath();String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";%><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"><html> <head> <base href="<%=basePath%>"> <title>My JSP 'index.jsp' starting page</title><meta http-equiv="pragma" content="no-cache"><meta http-equiv="cache-control" content="no-cache"><meta http-equiv="expires" content="0"> <meta http-equiv="keywords" content="keyword1,keyword2,keyword3"><meta http-equiv="description" content="This is my page"><!--<link rel="stylesheet" type="text/css" href="styles.css">--> </head> <body> <a href="<%=basePath%>servlet/DemoServlet?name=中国">Click</a> <form action="<%=basePath%>servlet/DemoServlet" method="post"> <input type="text" name="name"><br/> <input type="submit"/> </form> </body></html>
访问并测试:
这个过滤器应该被配置在所有过滤器的前面,就能解决全站的乱码了,这样就不用重复的编写解决乱码问题的代码了。
3.2、设置所有的jsp页面不缓存
因为jsp页面的有些内容是动态生成的,所有混成jsp页面的意义不大,我们通常会设置这些jsp页面不缓存。
第一步:开发Filter
package cn.zq.filter;import java.io.IOException;import javax.servlet.FilterChain;import javax.servlet.ServletException;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;public class DynamicPageCacheFilter extends HttpFilter {/** * */private static final long serialVersionUID = -5449451659530735173L;public void doFilter(HttpServletRequest request,HttpServletResponse response, FilterChain chain)throws IOException, ServletException {//设置3个响应头response.setHeader("pragma", "no-cache");response.setHeader("cache-control", "no-cache");response.setDateHeader("expires", 0);chain.doFilter(request, response);}}
第二步:配置web.xml
<filter> <filter-name>DynamicPageCacheFilter</filter-name> <filter-class> cn.zq.filter.DynamicPageCacheFilter</filter-class> </filter> <filter-mapping> <filter-name>DynamicPageCacheFilter</filter-name> <url-pattern>*.jsp</url-pattern> <dispatcher>REQUEST</dispatcher> <dispatcher>FORWARD</dispatcher> <dispatcher>INCLUDE</dispatcher> </filter-mapping
第三步:打开ie,清空所有的缓存,cookie,访问本项目的jsp文件看是否有缓存文件,将Filter拿到,再访问看是否有缓存文件。
控制是否缓存,也可以在jsp页面中加入这几个头
<meta http-equiv="pragma" content="no-cache"><meta http-equiv="cache-control" content="no-cache"><meta http-equiv="expires" content="0">
3.3、控制静态页面缓存(如html,图片)
第一步:编写Filter
package cn.zq.filter;import java.io.IOException;import java.util.Calendar;import javax.servlet.FilterChain;import javax.servlet.ServletException;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;public class StaticContentCacheFilter extends HttpFilter{/** * */private static final long serialVersionUID = 7660878144738222823L;@Overridepublic void doFilter(HttpServletRequest request,HttpServletResponse response, FilterChain chain)throws IOException, ServletException {/* *让图片缓存一个月,html文件缓存一个星期 , *具体的相关信息可以通过配置文件来配置。 */String requestURI = request.getRequestURI();long time = 0;int day = 0;if(requestURI.endsWith(".jpg")){day = 30;}else if(requestURI.endsWith(".html")){day = 7;}Calendar calendar = Calendar.getInstance();calendar.add(Calendar.DATE, day);time = calendar.getTimeInMillis();response.setDateHeader("expires", time);chain.doFilter(request, response);}}
第二步:配置
<filter> <filter-name>StaticContentCacheFilter</filter-name> <filter-class>cn.zq.filter.StaticContentCacheFilter</filter-class> </filter> <filter-mapping> <filter-name>StaticContentCacheFilter</filter-name><url-pattern>*.html</url-pattern><url-pattern>*.jpg</url-pattern> </filter-mapping>
第三步:测试
请求资源,再次请求。查看状态码为304,及缓存文件的日期为N天以后。这是返回的状态码:HTTP/1.1 304 Not Modified
3.4 验证用户是否登录
第一步:开发filter
package cn.zq.filter;import java.io.IOException;import java.io.PrintWriter;import javax.servlet.FilterChain;import javax.servlet.ServletException;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import javax.servlet.http.HttpSession;/** * 验证用户是否登录的过滤器 * @author Riccio Zhang * */public class LoginFilter extends HttpFilter{/** * */private static final long serialVersionUID = -6363929637537263967L;protected void doFilter(HttpServletRequest request,HttpServletResponse response, FilterChain chain)throws IOException, ServletException {HttpSession session = request.getSession();Object user = session.getAttribute("user");//没有找到user,则说明用户没有登录,转到登录页面让用户登录if(user == null){PrintWriter out = response.getWriter();out.print("<script>" + "alert('您还未登录!');" + "window.location.href='"+request.getContextPath()+"/login.jsp'" + "</script>");return;}chain.doFilter(request, response);}}第二步:开发登录功能,配置web.xml
package cn.zq.servlet;import java.io.IOException;import javax.servlet.ServletException;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;public class LoginServlet extends HttpServlet {/** * */private static final long serialVersionUID = 3059445154848670189L;public void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {//让用户退出登录request.getSession().invalidate();response.sendRedirect(request.getContextPath() + "/login.jsp");}public void doPost(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {/* * 让用户登录成功 */String username = request.getParameter("username");if(username == null || username.length() == 0){request.setAttribute("msg", "用户名不能为空!");request.getRequestDispatcher("/login.jsp").forward(request, response);return;}request.getSession().setAttribute("user", username);//重定向到主页response.sendRedirect(request.getContextPath() + "/page/index.jsp");}}
登录页面:/login.jsp
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%><%String path = request.getContextPath();String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";%><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"><html> <head> <title>My JSP 'login.jsp' starting page</title> <meta http-equiv="pragma" content="no-cache"><meta http-equiv="cache-control" content="no-cache"><meta http-equiv="expires" content="0"> <meta http-equiv="keywords" content="keyword1,keyword2,keyword3"><meta http-equiv="description" content="This is my page"> </head> <body> <p style="color:red;font: 12px;">${requestScope.msg }</p> <form action="<%=path%>/login" method="post"> username : <input type="text" name="username" ><br/> <input type="submit" value="Sign in"> </form> </body></html>
登录成功跳转页面:/page/index.jsp
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%><%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%><%String path = request.getContextPath();String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";%><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"><html> <head> <base href="<%=basePath%>"> <title>My JSP 'index.jsp' starting page</title><meta http-equiv="pragma" content="no-cache"><meta http-equiv="cache-control" content="no-cache"><meta http-equiv="expires" content="0"> <meta http-equiv="keywords" content="keyword1,keyword2,keyword3"><meta http-equiv="description" content="This is my page"><!--<link rel="stylesheet" type="text/css" href="styles.css">--> </head> <body> 欢迎你,<c:out value="${user }"></c:out><br/> <a href="<%=path%>/login">退出</a> </body></html>
<!-- 对/page/*进行过滤 --> <filter> <filter-name>LoginFilter</filter-name> <filter-class>cn.zq.filter.LoginFilter</filter-class> </filter> <filter-mapping> <filter-name>LoginFilter</filter-name><url-pattern>/page/*</url-pattern> </filter-mapping> <servlet-mapping> <servlet-name>DemoServlet</servlet-name> <url-pattern>/servlet/DemoServlet</url-pattern> </servlet-mapping> <servlet-mapping> <servlet-name>LoginServlet</servlet-name> <url-pattern>/login</url-pattern> </servlet-mapping>
第三步:测试,
在访问/page/index.jsp页面时,未登录是否会跳转到登录页面。登录时显示用户的名字。
3.5、自动登录
自动登录是为了让用户下次访问时,不用输入用户名和密码。将用户的信息保存到cookie中,下次直接从cookie中取。
第一步:开发登录页面
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%><%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%><%String path = request.getContextPath();String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";%><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"><html> <head> <title>My JSP 'login.jsp' starting page</title> <meta http-equiv="pragma" content="no-cache"><meta http-equiv="cache-control" content="no-cache"><meta http-equiv="expires" content="0"> <meta http-equiv="keywords" content="keyword1,keyword2,keyword3"><meta http-equiv="description" content="This is my page"> </head> <body><c:choose><c:when test="${empty user }"><p style="color:red;font: 12px;">${requestScope.msg }</p> <form action="<%=path%>/login" method="post"> username : <input type="text" name="username" ><br/> <fieldset> <legend>自动登录</legend> <input type="radio" name="day" value="0">不自动登录<br/> <input type="radio" name="day" value="1">1天<br/> <input type="radio" name="day" value="7">一个星期<br/> <input type="radio" name="day" value="30">一个月<br/> </fieldset> <input type="submit" value="Sign in"> </form></c:when><c:otherwise>欢迎您,<c:out value="${user }"></c:out><br/><a href="<c:url value='/login' />">退出</a></c:otherwise></c:choose> </body></html>
第二步:开发登录servlet
package cn.zq.servlet;import java.io.IOException;import java.net.URLEncoder;import javax.servlet.ServletException;import javax.servlet.http.Cookie;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;public class LoginServlet extends HttpServlet {/** * */private static final long serialVersionUID = 3059445154848670189L;public void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {//让用户退出登录request.getSession().invalidate();//删除cookieCookie cookie = new Cookie("autoLogin", "");/** * 0表示删除文件和缓存 * -1表示删除文件,但是还有缓存 */cookie.setMaxAge(0);cookie.setPath("/");response.addCookie(cookie);response.sendRedirect(request.getContextPath() + "/login.jsp");}public void doPost(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {/* * 让用户登录成功 */String username = request.getParameter("username");if(username == null || username.length() == 0){request.setAttribute("msg", "用户名不能为空!");request.getRequestDispatcher("/login.jsp").forward(request, response);return;}int day = 0;String auto = request.getParameter("day");try {day = Integer.parseInt(auto);} catch (Exception e) {}//对中文要进行编码Cookie cookie = new Cookie("autoLogin", URLEncoder.encode(username, request.getCharacterEncoding()));cookie.setMaxAge(day*24*3600);cookie.setPath("/");response.addCookie(cookie);request.getSession().setAttribute("user", username);response.sendRedirect(request.getContextPath() + "/page/index.jsp");}}
<servlet-mapping> <servlet-name>DemoServlet</servlet-name> <url-pattern>/servlet/DemoServlet</url-pattern> </servlet-mapping> <servlet-mapping> <servlet-name>LoginServlet</servlet-name> <url-pattern>/login</url-pattern> </servlet-mapping>
第三步:开发自动登录过滤器
package cn.zq.filter;import java.io.IOException;import java.net.URLDecoder;import javax.servlet.FilterChain;import javax.servlet.ServletException;import javax.servlet.http.Cookie;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import javax.servlet.http.HttpSession;public class AutoLoginFilter extends HttpFilter{/** * */private static final long serialVersionUID = 5891858915933022714L;@Overridepublic void doFilter(HttpServletRequest request,HttpServletResponse response, FilterChain chain)throws IOException, ServletException {/* * 优化:当用户手动登录或退出时,就不需要自动登录, * 并且用户已经登录,也不需要自动登录,自动登录的代码是 * 多此一举 */HttpSession session = request.getSession();String requestURI = request.getRequestURI();Object user = session.getAttribute("user");if(!requestURI.contains("/login") && user == null){//获取cookieCookie[] cookies = request.getCookies();if(cookies != null){for(Cookie c : cookies){if("autoLogin".equals(c.getName())){String username = c.getValue();username = URLDecoder.decode(username, request.getCharacterEncoding());session.setAttribute("user", username);break;}}}}chain.doFilter(request, response);}}
<filter> <filter-name>AutoLoginFilter</filter-name> <filter-class>cn.zq.filter.AutoLoginFilter</filter-class> </filter> <filter-mapping> <filter-name>AutoLoginFilter</filter-name><url-pattern>/*</url-pattern> </filter-mapping>
3.6、过滤非法语句(脏话)
在过滤器中,包装HttpServletRequest,修改getParameter方法
package cn.zq.filter;import java.io.IOException;import java.util.Arrays;import java.util.List;import javax.servlet.FilterChain;import javax.servlet.ServletException;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletRequestWrapper;import javax.servlet.http.HttpServletResponse;public class DirtyWordsFilter extends HttpFilter{private static final long serialVersionUID = -5025789414017693051L;public void doFilter(HttpServletRequest request,HttpServletResponse response, FilterChain chain)throws IOException, ServletException {request = new MyHttpServletRequest(request);chain.doFilter(request, response);}}class MyHttpServletRequest extends HttpServletRequestWrapper{List<String> dirtyWords = Arrays.asList(new String[]{"SB", "sb", "傻B", "2B"});public MyHttpServletRequest(HttpServletRequest request) {super(request);}public String getParameter(String name) {String value = super.getParameter(name);if(value != null && value.length() > 0){for(String dw : dirtyWords){value = value.replaceAll(dw, "***");}}return value;}}
3.7、全站压缩
实现对输出流的压缩:
在tomcat将数据输出到浏览器前,进行压缩,可以减少传送过去的数据,节约成本。如果在流量很少的情况下查看相同的内容和乐而不为呢?
思路:
- 在调用request.getOutputStream()或request.getWriter()时获取自己的输出流,将数据写到事先准备的缓冲中。
- 在输出完成后获取我们自己的缓冲数据
- 然后在对缓冲的数据进行压缩,在过滤器中将数据传输给浏览器
第一步:编写压缩数据的过滤器
package cn.zq.filter;import java.io.ByteArrayOutputStream;import java.io.IOException;import java.io.OutputStreamWriter;import java.io.PrintWriter;import java.util.zip.GZIPOutputStream;import javax.servlet.FilterChain;import javax.servlet.ServletException;import javax.servlet.ServletOutputStream;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import javax.servlet.http.HttpServletResponseWrapper;public class GzipFilter extends HttpFilter{/** * */private static final long serialVersionUID = 3410826595861585118L;public void doFilter(HttpServletRequest request,HttpServletResponse response, FilterChain chain)throws IOException, ServletException {String ac = request.getHeader("Accept-Encoding");//支持gzip压缩if(ac != null && ac.toLowerCase().indexOf("gzip") != -1){BufferedHttpServletResponse bRes = new BufferedHttpServletResponse(response);chain.doFilter(request, bRes);byte[] data = bRes.getData();System.out.println("->压缩前数据大小:" + data.length);ByteArrayOutputStream bos = new ByteArrayOutputStream();GZIPOutputStream gout = new GZIPOutputStream(bos);gout.write(data);gout.close();byte[] compressedData = bos.toByteArray();System.out.println("->压缩后的数据大小:"+compressedData.length);//设置头信息response.setContentLength(compressedData.length);response.setHeader("Content-Encoding", "gzip");ServletOutputStream out = response.getOutputStream();out.write(compressedData);}else{chain.doFilter(request, response);}}}class BufferedHttpServletResponse extends HttpServletResponseWrapper{private ByteArrayOutputStream buf = new ByteArrayOutputStream();private PrintWriter pw;public BufferedHttpServletResponse(HttpServletResponse response) {super(response);}public PrintWriter getWriter() throws IOException {pw = new PrintWriter(new OutputStreamWriter(buf, getResponse().getCharacterEncoding()));return pw;}public ServletOutputStream getOutputStream() throws IOException {ServletOutputStream sos = new ServletOutputStream() {public void write(int b) throws IOException {buf.write(b);}};return sos;}public byte[] getData(){if(pw != null){pw.close();}return buf.toByteArray();}}
第二步:配置对所有的jsp进行压缩
<filter> <filter-name>GzipFilter</filter-name> <filter-class>cn.zq.filter.GzipFilter</filter-class> </filter> <filter-mapping> <filter-name>GzipFilter</filter-name> <servlet-name>DemoServlet</servlet-name> <url-pattern>*.jsp</url-pattern> </filter-mapping>
第三步:测试压缩过滤器
使用压缩过滤器应该注意:应该只用这个压缩过滤器对文本进行压缩,例如jsp,html,css,js等进行压缩,对视频和图片的压缩率很低,不要用来压缩视频和图片,如果是下载,那也不应该用来压缩,这样不但压缩率很低,而且还有可能让服务器奔溃。
关于压缩过滤器的优化:
在doFilter方法中先将数据拿出来,然后放到GzipOutputStream中进行压缩,然后得到压缩后的字节再输出给客户端,这样2次都得到了字节,假如数据量较大,这2次都会占用较多的内存,能不能从包装的response拿出来时直接就是压缩过后的数据呢?改造后的代码如下:
package cn.zq.filter;import java.io.ByteArrayOutputStream;import java.io.IOException;import java.io.OutputStreamWriter;import java.io.PrintWriter;import java.util.zip.GZIPOutputStream;import javax.servlet.FilterChain;import javax.servlet.ServletException;import javax.servlet.ServletOutputStream;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import javax.servlet.http.HttpServletResponseWrapper;public class GzipFilter extends HttpFilter{/** * */private static final long serialVersionUID = 3410826595861585118L;public void doFilter(HttpServletRequest request,HttpServletResponse response, FilterChain chain)throws IOException, ServletException {String ac = request.getHeader("Accept-Encoding");//支持gzip压缩if(ac != null && ac.toLowerCase().indexOf("gzip") != -1){BufferedHttpServletResponse bRes = new BufferedHttpServletResponse(response);chain.doFilter(request, bRes);byte[] compressedData = bRes.getData();//设置头信息response.setContentLength(compressedData.length);response.setHeader("Content-Encoding", "gzip");ServletOutputStream out = response.getOutputStream();out.write(compressedData);}else{chain.doFilter(request, response);}}}class BufferedHttpServletResponse extends HttpServletResponseWrapper{private ByteArrayOutputStream buf = new ByteArrayOutputStream();private GZIPOutputStream gout;private PrintWriter pw;public BufferedHttpServletResponse(HttpServletResponse response) throws IOException {super(response);gout = new GZIPOutputStream(buf);}public PrintWriter getWriter() throws IOException {pw = new PrintWriter(new OutputStreamWriter(gout, getResponse().getCharacterEncoding()));return pw;}public ServletOutputStream getOutputStream() throws IOException {ServletOutputStream sos = new ServletOutputStream() {public void write(int b) throws IOException {gout.write(b);}};return sos;}public byte[] getData() throws IOException{if(pw != null){pw.close();}gout.close();return buf.toByteArray();}}
4.总结
本文的例子可以在这里下载”:http://download.csdn.net/detail/qq791967024/8446629
利用Filter能对请求和响应进行预处理,在到达目标组件之前,对强求进行处理,诸如:对请求头和响应头进行处理。充分的利用了包装器设计模式,对request或response进行包装,对其方法进行增强。假如我们拒绝某个请求,就可以写一个过滤器对不希望的请求不放行,即不执行chain.doFilter(request, response)方法,过滤器能帮助我们干很多的事情。
- java web filter 基础
- java web基础 --- Filter
- Java Web 基础 --- Filter 综述
- Java Web 基础 --- Filter 综述
- Java Web 基础 --- Filter 综述
- java web filter 之一 基础实现
- Java web filter 之一 基础实现
- java web filter 之一 基础实现
- java web基础 --- URL重定向Filter
- java web filter 之一 基础实现
- java web 之filter
- JAVA WEB FILTER(过滤器)
- Java-web Filter过滤器
- Java web----过滤器(Filter)
- Java Web Filter 详解
- JAVA WEB filter
- Java Web之Filter
- java web 过滤器filter
- 分支-12. 计算火车运行时间(15)*
- jQuery选择器代码详解(五)——实例说明tokenize的解析过程
- Java同步机制:synchronized,wait,notify
- WEB项目开发的一般流程
- 计算几何_多边形
- java web filter 基础
- jquery插件--ajaxfileupload.js上传文件原理分析
- Tomcat
- ThinkPHP3.1 行为扩展Behavior,tags标签位
- Java线程同步:生产者-消费者 模型(代码示例)
- 分支-13. 计算天数(15)*
- MATLAB R2014b 中文版 使用初了解
- redis 高可用
- 正则表达式的学后感