使用filter过滤请求

1.了解Filter的使用。

7.1.批量设置请求编码

编码问题会不会成为中国人学java的标志呢?

通过之前的讨论第2.2.2节“POST乱码”,我们知道为了避免提交数据的乱码问题,需要在每次使用请求之前设置编码格式。在你复制粘贴了无数次request.setCharacterEncoding("gb2312");后,有没有想要一劳永逸的方法呢?能不能一次性修改所有请求的编码呢?

用Filter吧,它的名字是过滤器,可以批量拦截修改servlet的请求和响应。

我们编写一个EncodingFilter.java,来批量设置请求编码。

packageanni;

importjava.io.IOException;

importjavax.servlet.Filter;

importjavax.servlet.FilterChain;

importjavax.servlet.FilterConfig;

importjavax.servlet.ServletException;

importjavax.servlet.ServletRequest;

importjavax.servlet.ServletResponse;

publicclassEncodingFilterimplementsFilter{

publicvoidinit(FilterConfigconfig)throwsServletException{}

publicvoiddestroy(){}

publicvoiddoFilter(ServletRequestrequest,

ServletResponseresponse,

FilterChainchain)

throwsIOException,ServletException{

request.setCharacterEncoding("gb2312");

chain.doFilter(request,response);

}

}

在此EncodingFilter实现了Filter接口,Filter接口中定义的三个方法都要在EncodingFilter中实现,其中doFilter()的代码实现主要的功能:为请求设置gb2312编码并执行chain.doFilter()继续下面的操作。

与servlet相似,为了让filter发挥作用还需要在web.xml进行配置。

<filter>

<filter-name>EncodingFilter</filter-name>

<filter-class>anni.EncodingFilter</filter-class>

</filter>

<filter-mapping>

<filter-name>EncodingFilter</filter-name>

<url-pattern>/*</url-pattern>

</filter-mapping>

filter标签部分定义使用的过滤器,filter-mapping标签告诉服务器把哪些请求交给过滤器处理。这里的/*表示所有请求,/表示根路径,*(星号)代表所有请求,加在一起就变成了根路径下的所有请求。

这样,所有的请求都会先被EncodingFilter拦截,并在请求里设置上指定的gb2312编码。

例子在lingo-sample/07-01目录下,这次我们不需要在test.jsp中为请求设置编码也可以得到正常的中文参数了,EncodingFilter圆满的完成了它的工作。

7.2.用filter控制用户访问权限

出于信息安全和其他一些原因的考虑,项目中的一些页面要求用户满足了一定条件之后才能访问。比如,让用户输入帐号和密码,如果输入的信息正确就在session里做一个成功登录的标记,其后在请求保密信息的时候判断session中是否有已经登录成功的标记,存在则可以访问,不存在则禁止访问。

如07-02例子中所示,进入首页看到的就是登录页面。

现在用户还没有登录,如果直接访问保密信息,就会显示无法访问保密信息的页面,并提醒用户进行注册。

返回登录页面后,输入正确的用户名和密码,点击登录。

后台程序判断用户名和密码正确无误后,在session中设置已登录的标记,然后跳转到保密信息页面。

我们要保护的页面是admin/index.jsp,为此我们在web.xml进行如下配置。

<filter>

<filter-name>SecurityFilter</filter-name>

<filter-class>anni.SecurityFilter</filter-class>

</filter>

<filter-mapping>

<filter-name>SecurityFilter</filter-name>

<url-pattern>/admin/*</url-pattern>

</filter-mapping>

定义SecurityFilter过滤器,让它过滤匹配/admin/*的所有请求,这就是说,对/admin/路径下的所有请求都会接受SecurityFilter的检查,那么SecurityFilter里到底做了些什么呢?

publicvoiddoFilter(ServletRequestrequest,

ServletResponseresponse,

FilterChainchain)

throwsIOException,ServletException{

HttpServletRequestreq=(HttpServletRequest)request;

HttpServletResponseres=(HttpServletResponse)response;

HttpSessionsession=req.getSession();

if(session.getAttribute("username")!=null){

chain.doFilter(request,response);

}else{

res.sendRedirect("../failure.jsp");

}

}

首先要将ServletRequest和ServletResponse转换成HttpServletRequest和HttpServletResponse,因为Filter本来设计成为多种协议服务,http协议仅仅是其中一部分。不过我们接触到的也只有http,而且也只有转换成对应HttpServletRequest和HttpServletResponse才能进行下面的session操作和页面重定向。

得到了http请求之后,可以获得请求对应的session,判断session中的username变量是否为null,如果不为null,说明用户已经登录,就可以调用doFilter继续请求访问的资源。如果为null,说明用户还没有登录,禁止用户访问,并使用页面重定向跳转到failure.jsp页面显示提示信息。

session中的username实在登录的时候设置进去的,值就是登录用户使用的用户名,详细代码可以参考07-02/WEB-INF/src/LoginServlet.java,登录和注销都写成了servlet并映射到/login.do和/logout.do这两个请求路径上。源代码和web.xml配置请自行参考07-02中的例子,这里就不复述了。

我们再来看看页面重定向的写法,res.sendRedirect()中使用的是"../failure.jsp",两个点(..)代表当前路径的上一级路径,这是因为SecurityFilter负责处理的是/admin/下的请求,而/failure.jsp的位置在/admin/目录的上一级,所以加上两个点才能正确跳转到failure.jsp。当然这里使用forward()也可以,但是要注意在不同路径下做请求转发会影响页面中相对路径的指向。相关讨论在:第3.4.2节“forward导致找不到图片”

7.3.filter所谓的特性

7.3.1.请求映射

filter-mapping和servlet-mapping都是将对应的filter或servlet映射到某个url-pattern上,当客户发起某一请求时,服务器先将此请求与web.xml中定义的所有url-pattern进行匹配,然后执行匹配通过的filter和servlet。

你可以使用三种方式定义url-pattern。

1.直接映射一个请求。

2.<servlet-mapping>

3.<servlet-name>ContactServlet</servlet-name>

4.<url-pattern>/contact.do</url-pattern>

5.</servlet-mapping>

第6.3节“使用servlet改写联系簿”中对servlet的映射,只有当请求是/contact.do的时候才会执行ContactServlet。/contact.do?id=1或/contact.do?method=list&id=1的请求也可以匹配到ContactServlet,这是因为根据http规范,请求的路径不包含问号以后的部分。

6.映射一个路径下的所有请求。

7.<servlet-mapping>

8.<servlet-name>EncodingFilter</servlet-name>

9.<url-pattern>/*</url-pattern>

10.</servlet-mapping>

第7.1节“批量设置请求编码”中这样使用星号(*)的形式,可以将某个路径下的所有请求都映射到EncodingFilter过滤器下,如果这个路径下还有子路径,那么子路径下的请求也会被EncodingFilter过滤。所以/*这种写法就会过滤应用下所有的请求。

如果像第7.2节“用filter控制用户访问权限”中那样把映射配置成/admin/*,就会只处理/admin/路径下的请求,不会处理根路径下的/index.jsp和/failure.jsp。

需要注意的是,这种写法必须以/开头,写成与绝对路径的形式,即便是映射所有请求也要写成/*,不能简化成*。

11.映射结尾相同的一类请求。

12.<servlet-mapping>

13.<servlet-name>ControllerServlet</servlet-name>

14.<url-pattern>*.do</url-pattern>

15.</servlet-mapping>

具体效果请参考07-03的例子,index.jsp中有四个链接,分别指向/a1.do,/a2.do,/xx/b1.do,/xx/yy/c1.do。

web.xml中的ControllerServlet会接收以.do结尾的请求,并使用forward将请求转发到/test.jsp。

点击/a1.do的情况。

点击/xx/yy/c1.do的情况。

这样做的一个好处是语义更清楚,只要看到以.do结尾的请求就知道肯定是交给ControllerServlet处理了,不管这个请求是在根路径还是子路径下,都会准确无误的找到对应的servlet。

缺点就是不同路径之间进行forward,jsp里就不能再使用相对路径了,所以我们在test.jsp中使用request.getContextPath()获得当前应用在服务器中的位置(例子中是/07-03)将相对路径都组装成绝对路径,这种用法在以后也会经常用到。

<%

pageContext.setAttribute("ctx",request.getContextPath());

%>

<p><ahref="${ctx}/index.jsp">返回</a></p>

最后需要注意的是,这种请求映射就不能指定某一路径了,它必须是以星号(*)开始字母结尾,不能写成/*.do的形式。

现在咱们也发现java的请求映射有多傻了,灵活配置根本是不可能的任务。

想要获得所有以user开头.do结尾的请求吗?user*.do在url-pattern是无法识别的,只能配置成*.do,再去servlet中对请求进行筛选。

想要让一个servlet负责多个请求吗?/user/*,/admin/*,*.do写在一起url-pattern也不认识,只能配成多个servlet-mapping。

<servlet-mapping>

<servlet-name>ControllerServlet</servlet-name>

<url-pattern>/user/*</url-pattern>

</servlet-mapping>

<servlet-mapping>

<servlet-name>ControllerServlet</servlet-name>

<url-pattern>/admin/*</url-pattern>

</servlet-mapping>

<servlet-mapping>

<servlet-name>ControllerServlet</servlet-name>

<url-pattern>*.do</url-pattern>

</servlet-mapping>

java的复杂性在此处显露无疑。实际使用时,最好不要依赖web.xml中的配置,在自己的类中实现灵活配置才是正途。

7.3.2.过滤链

其实在07-02这个例子里,我们使用了两个过滤器,EncodingFilter负责设置编码,SecurityFilter负责控制权限,

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值