首页 > 代码库 > 过滤器

过滤器

一、过滤器简介
a)Filter是SUN公司提供的一个资源过滤器接口,不同的Web容器有着不同的实现
b)Filter位于Web服务器和Web资源(Servlet/Jsp/Html)之间
c)过滤器过滤请求和响应二者
d)Filter可以进行简单判段,是否将请求放行给Web资源
e)Filter的开发过程:
1>>类 implements javax.servlet.Filter接口
2>>在web.xml文件配置Filter过滤器,告之Web服务器有过滤器的存在
web.xml中的配置信息如下:

<filter><filter-name>FilterDemo1</filter-name>(过滤器,可以随意,但要和filter-mapping中的name一致)<filter-class>cn.itcast.web.filter.FilterDemo1</filter-class>(过滤器全路径)</filter><filter-mapping><filter-name>FilterDemo1</filter-name>(过滤器名,同上)<url-pattern>/*</url-pattern>(过滤器能够过滤的资源路径,不是用户在URL中访问的路径) \\\*/</filter-mapping>

  



注意:
1)当访问一个web资源时,没有得到对应的结果,有可能是Filter没有放行资源
2)总结:写Filter一定要知道该Filter过滤哪个或哪些资源,不是所有的Filter都过滤/*的资源。 \\\*/

3 过滤器链
a)一个Web应用可以有0个或多个Filter,多个Filter的组合就是过滤器链
b)**多个Filter的执行先后顺序,与web.xml文件中配置的顺序有关
c)chain.doFilter(request,response)具有二义性:
>>如果有下一个Filter时,将请求转发给下一个Filter
>>如果无下一个Filter时,将请求转发给Web资源(serlvet/jsp/html)
d)可以将web资源中的一些公共代码,提取出来,放入Filter中

4 过滤器生命周期(Filter是一个单例)
空参构造() 1次
||
init() 1次
||
doFilter(请求,响应,过滤器链) N次,与请求次数有关
||
destory() 1次

*5 Filter的案例
a)简单的登录页面:设置请求和响应的编码方式
init()可以获取到初始化的配置信息,因此将POST请求方式的中文的编码进行设置,
防止提交中文和响应中文时的乱码问题。

 1 web.xml: 2 <filter> 3 <filter-name>FilterDemo1</filter-name> 4 <filter-class>com.suse.servlet.FilterDemo1</filter-class> 5 <init-param> 6 <param-name>charset</param-name> 7 <param-value>utf-8</param-value> 8 </init-param> 9 </filter>10 <filter-mapping>11 <filter-name>FilterDemo1</filter-name>12 <url-pattern>/*</url-pattern> //////*/13 </filter-mapping>14 15 fileterCode:16 private FilterConfig filterConfig;17 @Override18 public void init(FilterConfig filterConfig) throws ServletException {19 this.filterConfig = filterConfig;//设置filterConfig值20 }21 22 @Override23 public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException {24 String charSet = this.filterConfig.getInitParameter("charset");//得到配置文件的编码方式25 request.setCharacterEncoding(charSet);//设置请求时编码26 response.setContentType("text/html;charset="+charSet);//设置响应时编码27 filterChain.doFilter(request, response);//放行28 }

 

b)静态资源和动态资源进行不同的缓存处理,代码如下:

 1 if(uri!=null && uri.endsWith("jsp")){ 2 //NO3如果是动态资源,设置三个响应头通知浏览器不缓存 3 response.setDateHeader("Expires", -1);//IE 4 response.setHeader("Cache-Control", "no-cache"); 5 response.setHeader("Pragma", "no-cache"); 6 }else if(uri!=null && uri.endsWith("html")){ 7 //NO4如果是静态资源,缓存一定的时间 8 String strHtml = filterConfig.getInitParameter("html"); 9 long time = System.currentTimeMillis()+Integer.parseInt(strHtml)*1000;10 //time为毫秒值11 response.setDateHeader("expires",time); //第二个参数为保存到的时间,单位为毫秒12 response.setHeader("cache-control",time/1000+"");//第二个参数为保存到的时间,单位为秒13 response.setHeader("pragma",time/1000+"");//第二个参数为保存到的时间,单位为秒14 }

 

c)通过Filter实现URL级别的权限认证 ———— 对敏感目录进行认证
//访问admin目录下的admin.html文件需要带上用户名和密码进行验证

d)通过Filter和cookie实现自动登录功能

 1 xml配置: 2 <filter> 3 <filter-name>AutoLoginFilter</filter-name> 4 <filter-class>com.suse.filter.AutoLoginFilter</filter-class> 5 </filter> 6 <filter-mapping> 7 <filter-name>AutoLoginFilter</filter-name> 8 <url-pattern>/welcome.jsp</url-pattern> 9 </filter-mapping>10 第一次登录,设置cookie:11 String username = request.getParameter("username");12 String password = request.getParameter("password");13 if (username != null && password != null && username.equals("jack") && password.equals("123")) {14 Cookie cookie = new Cookie("usernameApassword", username + "_" + password);15 cookie.setMaxAge(10 * 60);16 response.addCookie(cookie);17 request.setAttribute("username", username);18 request.getRequestDispatcher("/welcome.jsp").forward(request, response);19 } else {20 response.getWriter().write("登录失败!");21 }22 第二次登录,解析cookie:23 @Override24 public void doFilter(ServletRequest req, ServletResponse res, FilterChain filterChain) throws IOException, ServletException {25 HttpServletRequest request = (HttpServletRequest) req;26 HttpServletResponse response = (HttpServletResponse) res;27 String username = null;28 String password = null;29 Cookie[] cookies = request.getCookies();30 for (Cookie cookie : cookies) {31 if (cookie.getName().equalsIgnoreCase("usernameApassword")) {32 String str = cookie.getValue();33 String[] usernameApassword = str.split("_");34 username = usernameApassword[0];35 password = usernameApassword[1];36 }37 }38 if (username != null && password != null && username.equals("jack") && password.equals("123")) {39 request.setAttribute("username", username);40 filterChain.doFilter(request, response);41 } else {42 request.getRequestDispatcher("/login.jsp").forward(request, response);43 }44 }

 

 

二、映射Filter的细节

a)在默认情况下,Filter只过滤Request的请求,即:web资源之间的转包和包含不过滤.
  MappingFilter::doFilter():A
  FromServlet::doGet()
  ToServlet::doGet()
  MappingFilter::doFilter():B

b)当需要过滤forward请求的资源时,可以设置dispatcher为FORWARD过滤方式
(注意:默认的REQUEST方式便不存在了)

  FromServlet::doGet()
  MappingFilter::doFilter():A
  ToServlet::doGet()
  MappingFilter::doFilter():B

在web.xml文件中配置代码如下:

<filter-nameapping><filter-name>MappingFilter</filter-name><url-pattern>/ToServlet</url-pattern> <dispatcher>FORWARD</dispatcher></filter-mapping>

 

c)即过滤REQUEST又过滤FORWARD时:

<filter-nameapping><filter-name>MappingFilter</filter-name><url-pattern>/ToServlet</url-pattern><dispatcher>FORWARD</dispatcher><dispatcher>REQUEST</dispatcher></filter-mapping>

 

d)当需要过滤include请求的资源时,可以设置dispatcher为INCLUDE过滤方式
e)当需要过滤error请求的资源时,可以设置dispatcher为ERROR过滤方式
注意:此时,一定在要web.xml文件声明错误代码或类型

<error-page><error-code>500</error-code><location>/sys_500.jsp</location></error-page>    <filter-mapping><filter-name>MappingFilter</filter-name><url-pattern>/*</url-pattern> */<dispatcher>ERROR</dispatcher></filter-mapping>

 


f)对于过滤Servlet资源时,即可使用url-pattern,又可以使用servlet-name
g)一个Filter可以过滤1个或N个资源,
即:一个<filter>,多个<filter-mapping>


*2 装饰设计模式
a)当某个类的某个方法不适应当前业务的需要
思路:
1》扩展父类的可供扩展的方法,可以使有,但不优
2》装饰设计模式(推荐)

开发步骤:
  1)写一个普通类(非web应用)或写一个普通类扩展[extends]一个父类(web应用)
  2)写一个需要被包装的实例变量
  3)通过构造方式为被包装的实例变量赋值
  4)对于不满足需求的方法,重写父类的相关方法
[可选]5)对于满足需求的方法,直接调用被包装的对象

code:

1,带有行号的BufferedReader的ReadLine方法的类:MyBufferedReader

 1 public class MyBufferedReader { 2  3 private BufferedReader bufferedReader; 4  5 private Integer lineNum; 6  7 public MyBufferedReader(BufferedReader bufferedReader) { 8 this.lineNum = 1; 9 this.bufferedReader = bufferedReader;10 }11 12 13 //包装readLine()方法14 public String readLine() throws IOException {15 String read = null;16 String line = null;17 if ((line = bufferedReader.readLine()) != null) {18 read = this.lineNum + ":" + line;19 lineNum++;20 }21 return read;22 }23 24 //关闭文件流,使用父类的方法来关闭流25 public void close() throws IOException {26 bufferedReader.close();27 } 28 }

 

2,增强Date类的toLocalString()方法,显示类似信息 "XXXX年XX月XX日 星期X XX:XX:XX"

 1 public class MyDate { 2  3 private Date date; 4  5 public MyDate(Date date) { 6 this.date = date; 7 } 8  9 public String toLocalString() {10 /*SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy年MM月dd日 EEEE HH:mm:ss ");11 return dateFormat.format(date);*/12 DateFormat dateFormat = DateFormat.getDateTimeInstance(DateFormat.FULL, DateFormat.MEDIUM, Locale.CHINA);13 return dateFormat.format(date); 14 }15 }

 

 


*3 Filter案例

a)对request进行装饰,完全解决get、post请求方式下的乱码问题

 1 //MyRequest.java 2 public class MyRequest extends HttpServletRequestWrapper { 3  4 private HttpServletRequest request; 5  6 public MyRequest(HttpServletRequest request) { 7 super(request); 8 this.request = request; 9 }10 11 @Override12 public String getParameter(String name) {13 String value = http://www.mamicode.com/null;14 //获取请求方式[GET/POST]15 String method = request.getMethod();16 if("GET".equals(method)) {17 try {18 value =http://www.mamicode.com/ request.getParameter(name);19 byte[] buf = value.getBytes("ISO8859-1");20 value = http://www.mamicode.com/new String(buf, "utf-8");21 } catch (UnsupportedEncodingException e) {22 e.printStackTrace();23 }24 } else {25 try {26 request.setCharacterEncoding("utf-8");27 value =http://www.mamicode.com/ request.getParameter(name);28 } catch (UnsupportedEncodingException e) {29 e.printStackTrace();30 }31 }32 return value;33 }34 35 //EncodingFilter36 public class EncodingFilter implements Filter {37 @Override38 public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {39 HttpServletRequest request = (HttpServletRequest) req;40 HttpServletResponse response = (HttpServletResponse) res;41 chain.doFilter(new MyRequest(request), response);42 }43 }

 

b)对request进行装饰,实现html标签的转义功能
//转义html符号

 1 private String filter(String message) { 2 if (message == null) 3 return (null); 4 char content[] = new char[message.length()]; 5 message.getChars(0, message.length(), content, 0); 6 StringBuffer result = new StringBuffer(content.length + 50); 7 for (int i = 0; i < content.length; i++) { 8 switch (content[i]) { 9 case ‘<‘:10 result.append("&lt;");11 break;12 case ‘>‘:13 result.append("&gt;");14 break;15 case ‘&‘:16 result.append("&amp;");17 break;18 case ‘"‘:19 result.append("&quot;");20 break;21 default:22 result.append(content[i]);23 }24 }25 return (result.toString());26 }

 


c)对response进行装饰[有一定难度]————压缩响应
思路: 缓存 --> 压缩 --> 输出

 1 //MyResponse 2 public class MyResponse extends HttpServletResponseWrapper { 3  4 private HttpServletResponse response; 5 private ByteArrayOutputStream bout = new ByteArrayOutputStream(); 6  7 public MyResponse(HttpServletResponse response) { 8 super(response); 9 this.response = response;10 }11 12 public ServletOutputStream getOutputStream() throws IOException {13 return new MyServletOutputStream(bout);14 }15 16 public byte[] getBuffer() {17 return bout.toByteArray();18 }19 20 }21 22 class MyServletOutputStream extends ServletOutputStream {23 24 private ByteArrayOutputStream bout;25 26 public MyServletOutputStream(ByteArrayOutputStream bout) {27 this.bout = bout;28 }29 30 @Override31 public void write(int arg0) throws IOException {32 }33 34 public void write(byte[] bytes) throws IOException {35 bout.write(bytes);36 bout.flush();37 }38 39 }40 41 42 //Filter43 public class GzipFilter implements Filter {44 @Override45 public void destroy() {46 47 }48 @Override49 public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {50 HttpServletRequest request = (HttpServletRequest) req;51 HttpServletResponse response = (HttpServletResponse) res;52 MyResponse myResponse = new MyResponse(response);53 54 chain.doFilter(request, myResponse);55 56 byte[] data =http://www.mamicode.com/ myResponse.getBuffer();57 System.out.println("压缩前:" + data.length);58 59 ByteArrayOutputStream bout = new ByteArrayOutputStream();60 GZIPOutputStream gout = new GZIPOutputStream(bout);61 gout.write(data);62 gout.flush();63 gout.close();64 65 data =http://www.mamicode.com/ bout.toByteArray();66 System.out.println("压缩后" + data.length);67 68 response.setHeader("content-encoding", "gzip");69 response.setHeader("content-length", data.length + "");70 71 response.getOutputStream().write(data);72 73 }74 75 @Override76 public void init(FilterConfig arg0) throws ServletException {77 // TODO Auto-generated method stub78 }79 }

 

c)简单Filter缓存

//单例,一个服务器只有一个ChcheFilterpublic class CacheFilter implements Filter {//示例变量【每个线程都共享】private Map<String, byte[]> cache = new HashMap<String, byte[]>();@Overridepublic void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {HttpServletRequest request = (HttpServletRequest) req;HttpServletResponse response = (HttpServletResponse) res;//获取到访问路径,判断是否已经缓存String uri = request.getRequestURI();byte[] data =http://www.mamicode.com/ cache.get(uri);//若没有缓存,从数据库拉取数据if (null == data) {System.out.println("没有缓存,从数据库拉取数据!");MyResponse myResponse = new MyResponse(response);//放行到servlet,从数据库拉取数据chain.doFilter(request, myResponse);//获取数据,并缓存到Map中data =http://www.mamicode.com/ myResponse.getBuffer();cache.put(uri, data);}//将数据回写给浏览器response.getOutputStream().write(data);}

 

 


4 总结Filter和Servlet

a)Filter通常完成一些非核心的业务流程控制
Servlet通常完成一些核心的业务流程控制

b)Filter通常完成一些对Servlet的请求和响应的预先处理控制。
Servlet却不行

c)Filter和Servlet是一个互补的技术,而不是替代技术

 


过滤器