Filter简介
- Filter 的基本功能是对Servlet容器调用Servlet的过程进行拦截,从而在Servlet进行响应处理的前后实现一些特殊的功能.
- 在Servlet API 中定义了三个接口类来供开发人员编写Filter 程序:Filter,FilterChain,FilterConfig
- Filter 程序是一个实现了Filter 接口的java类,与Servlet 程序相似,它由Servlet 容器进行调用和执行
- Filter 程序需要在web.xml 文件中进行注册和设置它所能拦截的资源:Filter 程序可以拦截JSP,Servlet,静态图片文件和静态html 文件.
一、Filter 是什么?
- Java Web 的一个重要组件,可以对发送到Servlet 的请求进行拦截并对响应也进行拦截.
- Filter 是实现了Filter 接口的java 类
- Filter 需要造web.xml文件中进行配置和映射
二、如何创建一个Filter并运行
- 创建一个Filter 类:实现Filter接口
public class HelloFilter implements Filter {/*** filter 创建之后立即被调用且只被调用一次* @param filterConfig* @throws ServletException*/@Overridepublic void init(FilterConfig filterConfig) throws ServletException {System.out.println("init……");}@Overridepublic void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {System.out.println("doFilter……");}@Overridepublic void destroy() {System.out.println("destory");}
}
- 在web.xml 文件中配置并映射该Filter
<!--注册Filter--><filter><filter-name>helloFilter</filter-name><filter-class>com.jm.javaweb.HelloFilter</filter-class></filter><!--映射Filter--><filter-mapping><filter-name>helloFilter</filter-name><url-pattern>/test.html</url-pattern></filter-mapping>
-
这里的url-pattern 指的是拦截的资源,当访问这个url 时这个Filter 被调用.
-
在同一个web.xml 文件中可以为同一个Filter 设置多个映射。若一个Filter 链中多次出现了同一个Filter 程序,则个Filter 程序的拦截处理过程将被多次执行.
三、Filter 相关的API
- Filter 接口
- init(FilterConfig filterConfig)方法
- 类似于Servlet 的init()方法,在创建Filter对象(Filter对象在Servlet 容器加载当前WEB应用时即被创建)后立即被调用且只被调用一次.该方法用于对当前的Filter 进行初始化工作.Filter 实例是单例的.
- FilterConfig 类似于ServletConfig
- 可以在web.xml文件中配置当前Filter 的初始化参数,配置方式也和Servlet 类似.
- doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
- 真正Filter 的逻辑代码需要编写在该方法中
- FilterChain:多个Filter,可以构成一个Filter链
- 这个方法是把请求传给Filter链的下一个Filter,若当前Filter是Filter 链的最后一个Filter,将把请求传给目标Servlet(JSP)
- 多个Filter 拦截的顺序和配置的顺序有关,靠前的先被调用.
- destroy() 方法
释放当前 Filter 所占用的资源的方法,在Filter 被销毁之前调用,且只被调用一次.
四、Filter 的基本工作原理
- 当在web.xml 中注册了一个Filter 来对某个Servlet程序进行拦截处理时,这个Filter 就成了Servlet容器与该Servlet 程序的通信线路上的一道关卡,该Filter 可以对Servlet 容器发送给Servlet 程序的请求和Servlet 程序回送给Servlet 容器的响应进行拦截,可以决定是否请求继续传递给Servlet程序,以及对请求和响应信息是否进行修改.
- 在一个web应用程序中可以注册多个Filter 程序,每个Filter 程序都可以对一个或一组Servlet 程序进行拦截
- 若有多个Filter 程序对某个Servlet程序的访问进行拦截,当针对该Servlet的访问请求到达时,web 容器将把这多个Filter 程序组合成一个Filter 链(过滤器链).Filter 链中各个Filter 的拦截顺序与它们在应用程序的web.xml 中的映射顺序一致
- 与开发Servlet 不同的是,Filter接口并没有相应的实现类可供继承,要开发过滤器,只能直接实现Filter 接口.
五、自定义一个专门处理Http的过滤器
/*** 自定义的Http接口,实现自Filter接口*/
public abstract class HttpFilter implements Filter {/*** 用于保存FilterConfig 对象*/private FilterConfig config;/*** 不建议子类直接覆盖,若直接覆盖,* 将有可能会导致filterConfig 成员变量初始化失败* @param filterConfig* @throws ServletException*/@Overridepublic void init(FilterConfig filterConfig) throws ServletException {this.config = filterConfig;init();}/*** 供子类继承的初始化方法,可以通过getFilterConfig 获取FilterConfig对象*/public void init() {}/*** 直接返回init(servletConfig) 的FilterConfig 对象* @return*/public FilterConfig getFilterConfig(){return config;}/*** 原生的doFilter 方法,在方法内部把ServletRequest和ServletResponse* 转为了HttpServeltRequest和HttpServletResponse* 并调用doFilter(HttpServletRequest servletRequest, HttpServletResponse servletResponse, FilterChain filterChain)* 若编写过滤器的过滤方法,不建议直接继承该方法,而建议继承* doFilter(HttpServletRequest servletRequest, HttpServletResponse servletResponse, FilterChain filterChain)** @param servletRequest* @param servletResponse* @param filterChain* @throws IOException* @throws ServletException*/@Overridepublic void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {HttpServletRequest req = (HttpServletRequest) servletRequest;HttpServletResponse resp = (HttpServletResponse) servletResponse;doFilter(req,resp,filterChain);}/*** 抽象方法,为Http请求定制,必须实现的方法* @param req* @param resp* @param chain* @throws IOException* @throws ServletException*/public abstract void doFilter(HttpServletRequest req, HttpServletResponse resp, FilterChain chain)throws IOException, ServletException;/*** 空的destory方法*/@Overridepublic void destroy() {}
}
六、过滤器链执行顺序
定义两个Filter,再定义一个JSP页面,通过访问JSP页面看这两个过滤器的执行顺序.
- HelloFilter
public class HelloFilter implements Filter {/*** filter 创建之后立即被调用且只被调用一次* @param filterConfig* @throws ServletException*/@Overridepublic void init(FilterConfig filterConfig) throws ServletException {System.out.println("init……");}@Overridepublic void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException {System.out.println("1.Before HelloFilter's chain.doFiltet");//放行chain.doFilter(req,resp);System.out.println("2.After HelloFilter's chain.doFilter");}@Overridepublic void destroy() {System.out.println("destory");}
}
- SecondFilter
public class SecondFilter implements Filter {@Overridepublic void init(FilterConfig filterConfig) throws ServletException {System.out.println("second init");}@Overridepublic void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {System.out.println("3.Before SecondFilter's doFiltet");//放行filterChain.doFilter(servletRequest,servletResponse);System.out.println("4.After SecondFilter's doFilter");}@Overridepublic void destroy() {System.out.println("second destory");}
}
- test.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html><head><title>Title</title></head><body><h4>Test Page</h4><%System.out.println("5.Test JSP");%></body>
</html>
执行结果:
1.Before HelloFilter’s chain.doFiltet
3.Before SecondFilter’s doFiltet
5.Test JSP
4.After SecondFilter’s doFilter
2.After HelloFilter’s chain.doFilter
Filter 的执行顺序和在web.xml 中的配置顺序有关, 越靠前,Filter 越先执行,在这个例子中,HelloFilter在SecondFilter之前配置,所以先执行HelloFilter。在访问JSP页面之前,请求会先经过两个Filter 处才能访问JSP页面,JSP 或者Servlet 给与响应之后响应又会再通过SecondFilter和HelloFilter在返回浏览器.
七、映射Filter
元素用于设置一个Filter 所负责拦截的资源。一个Filter 拦截的资源可通过两种方式来指定:Servlet名称和资源访问的请求路径
-
子元素用于设置filter 的注册名称。该值必须实在 元素中声明过的过滤器名字
-
设置filter 所拦截的请求路径(过滤器关联的URL样式)
-
指定过滤器所拦截的Servlet 名称
-
指定过滤器所拦截的资源被Servlet容器调用的方式,可以是REQUEST,INCLUDE,FORWARD和ERROR之一,默认REQUEST,可以设置多个子元素用来指定Filter 对资源的多种调用方式进行拦截.
- REQUEST:当用户直接访问页面时,Web 容器将会调用过滤器。如果目标资源时通过RequestDispatcher的include()或forward()方法访问时,通过GET或POST请求直接访问
- INCLUDE:如果目标资源时通过RequestDispatcher的include()方法访问时,那么该过滤器将会被调用,除此之外,该过滤器不会被调用
<jsp:include file=""/>
- FORWARD:forward()方法访问时,那么该过滤器将会被调用,除此之外,该过滤器不会被调用
<jsp:forward page=""/> <%@ page errorPage=""/>
- ERROR:如果目标资源时通过声明式异常处理机制调用时,那么该过滤器将会被调用,除此之外,过滤器不会被调用.
//在web.xml 文件中通过error-page节点进行声明 <error-page><exception-type>java.lang.ArithmeticException</exception-type><location>/test.jsp</location> </error-page>
八、应用
- 使浏览器不缓存页面的过滤器
- 有3个HTTP响应头字段都可以禁止浏览器缓存当前页面,它们在Servlet 中的示例代码如下:
response.setDtaeHeader("Expires",-1);response.setHeader("Cache-Control","no-cache");response.setHeader("Pragma","no-cache");
- 并不是所有的浏览器都能完全支持上面的三个响应头,因此最好使同时使用上面的三个响应头
public class NoCacheFilter extends HttpFilter {@Overridepublic void doFilter(HttpServletRequest req, HttpServletResponse resp, FilterChain chain) throws IOException, ServletException {System.out.println("cacheFilter's doFilter...");resp.setDateHeader("Expires",-1);resp.setHeader("Cache-Control","no-cache");resp.setHeader("Pragma","no-cache");chain.doFilter(req,resp);}
}
- 字符编码过滤器
通过配置参数上encoding 指明使用何种字符编码,以处理Html Form 请求参数的中文问题
public class EncodingFilter extends HttpFilter {private String encoding;@Overridepublic void doFilter(HttpServletRequest req, HttpServletResponse resp, FilterChain chain) throws IOException, ServletException {System.out.println("encoding's Filter");encoding = getFilterConfig().getServletContext().getInitParameter("encoding");req.setCharacterEncoding(encoding);chain.doFilter(req,resp);}
}
web.xml
<context-param><param-name>encoding</param-name><param-value>UTF-8</param-value>
</context-param><filter><filter-name>encoding</filter-name><filter-class>com.jm.encoding.EncodingFilter</filter-class></filter><filter-mapping><filter-name>encoding</filter-name><url-pattern>/encoding/*</url-pattern></filter-mapping>
- 检测用户是否登录的浏览器
- 情景:系统中某些页面只有在正常登录后才可以使用,用户请求这些页面时要检查session 中有无该用户信息,但在所有必要的页面加上session 的判断相当麻烦的事情.
- 解决方案:编写一个用于检测用户是否登录的过滤器,如果用户未登录,则重定向到指定的登录页面.
- 要求:需检查的在Session 中保存的关键字;如果用户未登录,需重定向到指定的页面(URL不包括ContextPath),不做检查的URL列表(以分号分开,并且URL中不包括ContextPath)都要采取可配置的方式.
页面
- login.jsp:登录页面,请求提交到doLogin
<%@ page contentType="text/html;charset=UTF-8" language="java" %><html><head><title>Title</title></head><body><form action="doLogin.jsp" method="post">username:<input type="text" name="username"/><input type="submit" value="Submit"/></form></body>
</html>
- doLogin.jsp:判断用户信息,存入Session.
<%@ page contentType="text/html;charset=UTF-8" language="java" %><html><head><title>Title</title></head><body><%//1.获取用户的登录信息String username = request.getParameter("username");//2.若登录信息完整,则把登录信息放到session里面if(username != null && !username.equals("")){session.setAttribute(application.getInitParameter("userSessionKey"),username);//3.重定向到list.jspresponse.sendRedirect("list.jsp");}else{response.sendRedirect("login.jsp");}%></body>
</html>
- list.jsp:list 列表,所有可以访问的页面链接.用户可不登录直接访问
<%@ page contentType="text/html;charset=UTF-8" language="java" %><html><head><title>Title</title></head><body><a href="a.jsp"/>AAA<br><br><a href="b.jsp"/>BBB<br><br><a href="c.jsp"/>CCC<br><br><a href="d.jsp"/>DDD<br><br><a href="e.jsp"/>EEE<br><br></body>
</html>
- web.xml
<!--用户信息放入session中键的名字--><context-param><param-name>userSessionKey</param-name><param-value>USERSESSIONKEY</param-value></context-param><!--若未登录,需重定向的页面--><context-param><param-name>redirectPage</param-name><param-value>/login/login.jsp</param-value></context-param><!--不需要拦截或检查的url列表:list login a--><context-param><param-name>uncheckedUrls</param-name><param-value>/login/a.jsp,/login/list.jsp,/login/login.jsp,/login/doLogin.jsp,/login/b.jsp</param-value></context-param><filter><filter-name>loginFilter</filter-name><filter-class>com.jm.login.LoginFilter</filter-class></filter><filter-mapping><filter-name>loginFilter</filter-name><url-pattern>/login/*</url-pattern></filter-mapping>
- LoginFilter
public class LoginFilter extends HttpFilter {//1.获取web.xml获取sessionkey,redirectUrl,uncheckUrlsprivate String sessionKey;private String redirectUrl;private String uncheckedUrls;@Overridepublic void init() {ServletContext context = getFilterConfig().getServletContext();//sessionKey = context.getInitParameter("userSessionKey");//重定向页面redirectUrl = context.getInitParameter("redirectPage");//不用检查的urluncheckedUrls = context.getInitParameter("uncheckedUrls");}@Overridepublic void doFilter(HttpServletRequest req, HttpServletResponse resp, FilterChain chain) throws IOException, ServletException {//1.获取请求的URLString url = req.getRequestURL().toString();String uri = req.getRequestURI();String servletPath = req.getServletPath();//2.检查1获取的servletPath是否为不需要检查的url中的一个,如果是,则直接放行,方法结束List<String> urls = Arrays.asList(uncheckedUrls.split(","));if(urls.contains(servletPath)){chain.doFilter(req,resp);return;}//3.从session 中获取sessionKey 对应的值,若值不存在,则重定向到redirectUrlObject user = req.getSession().getAttribute(sessionKey);if(user == null){resp.sendRedirect(req.getContextPath()+redirectUrl);}//4.若存在,则放行,允许访问chain.doFilter(req,resp);}
}
-
使用Filter 完成一个简单的权限模型:
需求: -
管理权限
- 查看某人的权限
- 修改某人的权限
实现:
login.jsp
<html><head><title>Title</title></head><body><form action="${pageContext.request.contextPath}/loginServlet?method=login" method="post">name:<input type="text" name="name"/><input type="submit" value="Submit"/></form></body>
</html>
authority-manager.jsp
有一个text文本框,供输入username,提交后,使用checkbox 显示当前用户所有的权限的信息.
检查request 中是否有user信息,若有,则显示xxx的权限为:对应的权限的checkbox 打上对号. 提示,页面上需要两层循环的方式来筛选出被选择的权限.
<html><head><title>Title</title></head><body><center><br><br><form action="${pageContext.request.contextPath}/AuthorityServlet?method=getAuthorities" method="post">name:<input type="text" name="username"/><input type="submit" value="Submit"/></form><c:if test="${requestScope.user != null}">${requestScope.user.username} 的权限是:<br><br><form action="${pageContext.request.contextPath}/AuthorityServlet?method=updateAuthority" method="post"><input type="hidden" name="username" value="${requestScope.user.username}"/><c:forEach items="${authorities}" var="auth"><c:set var="flag" value="false"/><c:forEach items="${user.authorities}" var="ua"><c:if test="${ua.url == auth.url}"><c:set var="flag" value="true"/></c:if></c:forEach><c:if test="${flag == true}"><input type="checkbox" name="authority" value="${auth.url}" checked="checked">${auth.displayName}</c:if><c:if test="${flag == false}"><input type="checkbox" name="authority" value="${auth.url}">${auth.displayName}</c:if><br><br></c:forEach><input type="submit" value="Update"/></form></c:if></center></body>
</html>
- 对访问进行权限控制:有权限则可以访问,否则提示:没有对应的权限,请返回
public class AuthorityFilter extends HttpFilter {@Overridepublic void doFilter(HttpServletRequest req, HttpServletResponse resp, FilterChain chain) throws IOException, ServletException {
// - 获取ServletPathString servletPath = req.getServletPath();//不需要被拦截的url列表List<String> uncheckUrls = Arrays.asList("/authority/403.jsp", "/authority/articles.jsp", "/authority/authority-manager.jsp", "/authority/login.jsp", "/authority/logout.jsp");if(uncheckUrls.contains(servletPath)){chain.doFilter(req,resp);return;}// 在用户已经登录(可使用用户是否登录的过滤器)的情况下,获取用户信息:session.getAttribute("user");User user = (User) req.getSession().getAttribute("user");if(user == null){resp.sendRedirect(req.getContextPath()+"/authority/login.jsp");}
// 在获取用户所具有的权限的信息:List<Authority>List<Authority> authorities = user.getAuthorities();// 检验用户是否有请求servletPath的权限:可以思考除了遍历之外,有没有更好的实现方式Authority authority = new Authority(null, servletPath);// 若有权限则:响应(这里因为contains方法判断要使用equals,所以我们必须要重写equals方法)if (authorities.contains(authority)) {chain.doFilter(req,resp);return;}
// 若没有权限:重定向到403.jspresp.sendRedirect(req.getContextPath()+"/authority/403.jsp");}
}
- 用户若登录,需要把用户信息(User)放入到 HttpSession 中
- 在检验权限之前,需要判断用户是否已经登录.
3.管理权限
- 封装权限信息:Authority
public class Authority {//显示到页面上的权限的名字private String displayName;//权限对应的URL地址:一个权限对应一个URL private String url;private String url;public Authority() {}public Authority(String displayName, String url) {this.displayName = displayName;this.url = url;}public String getDisplayName() {return displayName;}public void setDisplayName(String displayName) {this.displayName = displayName;}public String getUrl() {return url;}public void setUrl(String url) {this.url = url;}@Overridepublic boolean equals(Object o) {if (this == o)return true;if (o == null || getClass() != o.getClass())return false;Authority authority = (Authority) o;return Objects.equals(url, authority.url);}@Overridepublic int hashCode() {return Objects.hash(url);}
}
- 封装用户信息:User
public class User {private String username;private List<Authority> authorities;public User() {}public User(String username, List<Authority> authorities) {this.username = username;this.authorities = authorities;}public String getUsername() {return username;}public void setUsername(String username) {this.username = username;}public List<Authority> getAuthorities() {return authorities;}public void setAuthorities(List<Authority> authorities) {this.authorities = authorities;}
}
- 创建一个UseryDao:
public class UserDao {private static Map<String,User> users;private static List<Authority> authorities = null;static {//用户所有的权限authorities = new ArrayList<Authority>();authorities.add(new Authority("Article-1","/authority/article-1.jsp"));authorities.add(new Authority("Article-2","/authority/article-2.jsp"));authorities.add(new Authority("Article-3","/authority/article-3.jsp"));authorities.add(new Authority("Article-4","/authority/article-4.jsp"));users = new HashMap<String,User>();//给AAA用户添加12权限User user1 = new User("AAA",authorities.subList(0,2));users.put("AAA", user1);//给BBB用户添加34权限user1 = new User("BBB",authorities.subList(2,4));users.put("BBB",user1);}//通过用户名获取用户public User get(String username){return users.get(username);}//更新用户权限public void update(String username,List<Authority> authorities){System.out.println("username:"+username);if(users.get(username) == null){System.out.println(username+"不存在");}users.get(username).setAuthorities(authorities);}//获取所有的权限信息public List<Authority> getAuthorities(){return authorities;}public List<Authority> getAuthorities(String[] urls) {//新的权限集合List<Authority> authorities2 = new ArrayList<Authority>();for(Authority authority:authorities){if(urls != null){for(String url:urls){if(url.equals(authority.getUrl())){authorities2.add(authority);}}}}return authorities2;}
}
- Servlet
authority-manager.jsp 提交表单后get方法:获取表单的请求参数:username,再根据username获取User 信息.把user放入到request 请求域中,转发到authority-manager.jsp.
authority-manager.jsp 修改权限的表单提交后update方法:获取请求参数:username,authority(多选);把权限封装为List;调用UserDao 的update() 方法实现权限的修改;重定向到authority-manager.jsp
public class AuthorityServlet extends HttpServlet {@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {String methodName = req.getParameter("method");System.out.println(methodName);try {Method method = getClass().getMethod(methodName, HttpServletRequest.class, HttpServletResponse.class);method.invoke(this, req, resp);} catch (Exception e) {e.printStackTrace();}}private UserDao userDao = new UserDao();//获取用户全选public void getAuthorities(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {String username = req.getParameter("username");User user = userDao.get(username);req.setAttribute("user", user);req.setAttribute("authorities",userDao.getAuthorities());req.getRequestDispatcher("/authority/authority-manager.jsp").forward(req, resp);}//更新权限public void updateAuthority(HttpServletRequest req,HttpServletResponse resp) throws IOException {String username = req.getParameter("username");String[] authorities = req.getParameterValues("authority");List<Authority> authorityList = userDao.getAuthorities(authorities);userDao.update(username,authorityList);resp.sendRedirect(req.getContextPath()+"/authority/authority-manager.jsp");}
}
5.为论坛过滤不雅文字和HTML特殊字符
- 开发论坛模块时要解决以下两个问题:
- 用户回复发帖时可能会插入如HTML代码(例如:<,>等),这可能会破坏论坛的正常显示,也可能会带来安全隐患
- 某些用户在回复时可能会输入不雅句子,这些字句会给论坛带来不好的影响
- 实现对不雅文字的可配置
- 要求:不雅文字及其替代内容实现可配置
Serevlet API 也提供了一个HttpSerevletRequestWrapper类来包装原始的request 对象.
HttpServletRequestWrapper 类实现了HttpServletRequest 接口中的所有方法.
这些方法的内部实现都是仅仅调用了一下所包装的 request 对象的对应方法.
相类似Servlet API 也提供了一个HttpServletResponseWrapper 类来包装原始的response 对象.
包装类 ServletRequest 实现
public class HttpServletRequestWrapper extends ServletRequestWrapper implements HttpServletRequest {public HttpServletRequestWrapper(HttpServletRequest request) {super(request);}private HttpServletRequest _getHttpServletRequest() {return (HttpServletRequest)super.getRequest();}public String getAuthType() {return this._getHttpServletRequest().getAuthType();}public Cookie[] getCookies() {return this._getHttpServletRequest().getCookies();}public long getDateHeader(String name) {return this._getHttpServletRequest().getDateHeader(name);}……
}
作用:对HttpServletRequest 或HttpServletResponse 的某一个方法进行修改或者增强
MyHttpServletRequest
public class MyHttpServletRequest extends HttpServletRequestWrapper {public MyHttpServletRequest(HttpServletRequest request) {super(request);}@Overridepublic String getParameter(String name) {String val = super.getParameter(name);if(val != null && val.contains(" fuck ")){val = val.replace("fuck","****");}return val;}
}
使用:在Filter ,利用HttpServletRequest 替换传入的HttpServletRequest
public void doFilter(HttpServletRequest req, HttpServletResponse resp, FilterChain chain) throws IOException, ServletException {//1.获取请求content参数的值System.out.println(req);HttpServletRequest request = new MyHttpServletRequest(req);String content = request.getParameter("content");}chain.doFilter(request,resp);}
此时到达Servlet 或JSP的HttpServletRequest 实际上是MyHttpServletRequest
HttpServletRequestWrapper
1.为什么需要 HttpServletRequestWrapper
需要改变从Servlet 容器(可能是任何的Servlet容器)中传入的HttpServletRequest 对象的某个行为,该怎么办?
- 继承HttpServletRequest接口的Servlet容器的实现类,但就和具体的容器相耦合
- 提供HttpServletRequest接口的实现类,很麻烦,而且也需要和具体的容器相耦合。
- 使用装饰器设计模式
- 提供一个类,该类实现HttpServletRequest 接口
- 传入具体的一容器实现的HttpServletRequest 接口的实现类作为上述类的一个成员变量
- 使用HttpServletRequest 成员变量来实现HttpServletRequest 接口的所有方法。
- 提供HttpServletRequestWrapper 的实现类,实现具体的方法.