过滤器
一、过滤器简介
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 @Override 18 public void init(FilterConfig filterConfig) throws ServletException { 19 this.filterConfig = filterConfig;//设置filterConfig值 20 } 21 22 @Override 23 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 @Override 24 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 @Override 12 public String getParameter(String name) { 13 String value = null; 14 //获取请求方式[GET/POST] 15 String method = request.getMethod(); 16 if("GET".equals(method)) { 17 try { 18 value = request.getParameter(name); 19 byte[] buf = value.getBytes("ISO8859-1"); 20 value = new String(buf, "utf-8"); 21 } catch (UnsupportedEncodingException e) { 22 e.printStackTrace(); 23 } 24 } else { 25 try { 26 request.setCharacterEncoding("utf-8"); 27 value = request.getParameter(name); 28 } catch (UnsupportedEncodingException e) { 29 e.printStackTrace(); 30 } 31 } 32 return value; 33 } 34 35 //EncodingFilter 36 public class EncodingFilter implements Filter { 37 @Override 38 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("<"); 11 break; 12 case '>': 13 result.append(">"); 14 break; 15 case '&': 16 result.append("&"); 17 break; 18 case '"': 19 result.append("""); 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 @Override 31 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 //Filter 43 public class GzipFilter implements Filter { 44 @Override 45 public void destroy() { 46 47 } 48 @Override 49 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 = 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 = 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 @Override 76 public void init(FilterConfig arg0) throws ServletException { 77 // TODO Auto-generated method stub 78 } 79 }
c)简单Filter缓存
//单例,一个服务器只有一个ChcheFilter public class CacheFilter implements Filter { //示例变量【每个线程都共享】 private Map<String, byte[]> cache = new HashMap<String, byte[]>(); @Override public 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 = cache.get(uri); //若没有缓存,从数据库拉取数据 if (null == data) { System.out.println("没有缓存,从数据库拉取数据!"); MyResponse myResponse = new MyResponse(response); //放行到servlet,从数据库拉取数据 chain.doFilter(request, myResponse); //获取数据,并缓存到Map中 data = myResponse.getBuffer(); cache.put(uri, data); } //将数据回写给浏览器 response.getOutputStream().write(data); }
4 总结Filter和Servlet
a)Filter通常完成一些非核心的业务流程控制
Servlet通常完成一些核心的业务流程控制
b)Filter通常完成一些对Servlet的请求和响应的预先处理控制。
Servlet却不行
c)Filter和Servlet是一个互补的技术,而不是替代技术