Servlet中的过滤器Filter是实现了javax.servlet.Filter接口的服务器端程序,主要的用途是过滤字符编码、做一些业务逻辑判断等。其工作原理是,只要你在web.xml文件配置好要拦截的客户端请求,它都会帮你拦截到请求,此时你就可以对请求或响应(Request、Response)统一设置编码,简化操作;同时还可进行逻辑判断,如用户是否已经登陆、有没有权限访问该页面等等工作。它是随你的web应用启动而启动的,只初始化一次,以后就可以拦截相关请求,只有当你的web应用停止或重新部署的时候才销毁。
简单来说,过滤器filter的作用,它就相当于公路上的一个关卡,对要通过的拦截下来进行相关操作或放行。
dofilter作用【request -> filter1 -> filter2 ->filter3 -> …. -> request resource。】
看一个简单的没有filter的登录例子,当用户名和密码都是admin时,能跳转到success页面,否则到fail页面。
1、eclipse建立一个web project ,结果目录如下
其中,jsp很简单。在login.jsp为一个form表单
1 <form action="<%=request.getContextPath() %>/servlet/LoginServlet" method="post">
2 用户名:<input type="text" name="username">
3 密码:<input type="password" name="password">
4 <input type="submit" value="提交">
5 </form>
配置文件xml中为
1 <servlet>
2 <description>This is the description of my J2EE component</description>
3 <display-name>This is the display name of my J2EE component</display-name>
4 <servlet-name>LoginServlet</servlet-name>
5 <servlet-class>com.imooc.serlvet.LoginServlet</servlet-class>
6 </servlet>
7
8 <servlet-mapping>
9 <servlet-name>LoginServlet</servlet-name>
10 <url-pattern>/servlet/LoginServlet</url-pattern>
11 </servlet-mapping>
LoginServlet.java中主要是
1 public void destroy() {
2 super.destroy(); // Just puts "destroy" string in log
3 // Put your code here
4 }
5 public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
6 String username = request.getParameter("username");
7 String password = request.getParameter("password");
8
9 System.out.println(username);
10
11 if("admin".equals(username) && "admin".equals(password)){
12 //校验通过
13 HttpSession session = request.getSession();
14 session.setAttribute("username", username);
15 response.sendRedirect(request.getContextPath()+"/sucess.jsp");
16 }else{
17 //校验失败
18 response.sendRedirect(request.getContextPath()+"/fail.jsp");
19 }
20
21 }
22 public void init() throws ServletException {
23 // Put your code here
24 }
运行后浏览器地址栏进入:…8080/LoginFilter/login.jsp
当用户名和密码都输入admin,按下submit,就会进入action对应的servlet,然后重定向到success.jsp页面;用户名密码不对就到fail.jsp。
小例子结束。
仔细想会想到,可以直接在地址输入8080/LoginFilter/success.jsp不就进入了成功的页面了吗,还有类似页面。这样时不安全的,所以要加入拦截器
2、在此项目加入拦截器filter,也就是对其url进行拦截,不让其用url随便访问,结构中多了个类文件
在xml中添加
1 <filter>
2 <filter-name>LoginFilter</filter-name>
3 <filter-class>com.imooc.filter.LoginFilter</filter-class>
4 </filter>
5 <filter-mapping>
6 <filter-name>LoginFilter</filter-name>
7 <url-pattern>/success.jsp</url-pattern>
8 </filter-mapping>
这样,url输入…8080/LoginFilter/success.jsp就会进行拦截,进入LoginFilter.java
里面主要为:
1 @Override
2 public void destroy() {
3 System.out.println("销毁...");
4 }
5 @Override
6 public void doFilter(ServletRequest arg0, ServletResponse arg1, FilterChain arg2) throws IOException, ServletException {
7 HttpServletRequest request = (HttpServletRequest) arg0;
8 HttpServletResponse response = (HttpServletResponse) arg1;
9 HttpSession session = request.getSession();
10 //因为登录后保存了username,所以可以先检查username判断是否登录
11 if(session.getAttribute("username")!=null){
12 arg2.doFilter(arg0, arg1);//已登录,则放行,
13 }else{
14 response.sendRedirect("login.jsp");//未登录,重定向到登录页面
15 }
16 }
17 public void init(FilterConfig arg0) throws ServletException
18 {
19
20 System.out.println("初始化...");
21
22 }
【过滤器Filter生命周期:init()->doFilter()->destroy()】
【 出现了 arg.doFailter(arg0,arg1),他表示好像放行、通过这样,继续往下到LoginServlet.java中,如果没有写 arg.doFailter(arg0,arg1)。那就是,把他拦截下来后,什么都不做,也不让它通过,那就会卡在那里不能往下了。】
【在这里只是request -> filter1 -> request resource。】
这样的思路,就可以拦截一些不能随便访问的页面,但如果这类页面很多,可访问的页面相对少,则可以把拦截的地址改为/*,也就是
1 <filter-mapping>
2 <filter-name>LoginFilter</filter-name>
3 <url-pattern>/success.jsp</url-pattern>
4 </filter-mapping>
5 改成
6 <filter-mapping>
7 <filter-name>LoginFilter</filter-name>
8 <url-pattern>/*</url-pattern>
9 </filter-mapping>
再对本不用拦截的,比如login.jsp 在LoginFilter进行判断
1 public void doFilter(ServletRequest arg0, ServletResponse arg1, FilterChain arg2) throws IOException, ServletException {
2 HttpServletRequest request = (HttpServletRequest) arg0;
3 HttpServletResponse response = (HttpServletResponse) arg1;
4 HttpSession session = request.getSession();
5 //如果是login.jsp,则不拦截,直接放行,不用进行其他操作
6 if(request.getRequestURI().indexOf("login.jsp")!=-1 ){
7 arg2.doFilter(arg0, arg1);
8 return;
9 }
10
11 //因为登录后保存了username,所以可以先检查username判断是否登录
12 if(session.getAttribute("username")!=null){
13 arg2.doFilter(arg0, arg1);//已登录,则放行,
14 }else{
15 response.sendRedirect("login.jsp");//未登录,重定向到登录页面
16 }
仔细想想,其他问题也来了,因为拦截所有页面,所以按下submit时都过不去(action=”<%=request.getContextPath() %>/servlet/LoginServlet” 也要拦截,过不去),则要添加一些不需拦截的url
复制代码
1 //如果是下面3个url,则不拦截,直接放行,不用进行其他操作
2 if(request.getRequestURI().indexOf("login.jsp")!=-1
3 ||request.getRequestURI().indexOf("servlet/LoginServlet")!=-1
4 ||request.getRequestURI().indexOf("fail.jsp")!=-1
5 ){
6 arg2.doFilter(arg0, arg1);
7 return;
8 }
但这样也有问题,当不需拦截的url多了,if语句也屡屡需要修改,很麻烦,则可以用 FilterConfig对象。先在xml添加配置,有新的不需要拦截的url,秩序在配置里添加即可。
复制代码
1 <filter>
2 <filter-name>LoginFilter</filter-name>
3 <filter-class>com.imooc.filter.LoginFilter</filter-class>
4 <init-param>
5 <param-name>noLoginPaths</param-name>
6 <param-value>login.jsp;fail.jsp;LoginServlet</param-value> //在此添加不需拦截的url
7 </init-param>
8 <init-param>
9 <param-name>charset</param-name> //防止中文乱码
10 <param-value>UTF-8</param-value>
11 </init-param>
12 </filter>
13 <filter-mapping>
14 <filter-name>LoginFilter</filter-name>
15 <url-pattern>/*</url-pattern>
16 </filter-mapping>
在dofilter中得到此对象 LoginFilter.java
1 public class LoginFilter implements Filter {
2 private FilterConfig config; //这里定义一下
3 @Override
4 public void destroy() {
5
6 }
7 @Override
8 public void doFilter(ServletRequest arg0, ServletResponse arg1, FilterChain arg2) throws IOException, ServletException {
9
10 HttpServletRequest request = (HttpServletRequest) arg0;
11 HttpServletResponse response = (HttpServletResponse) arg1;
12 HttpSession session = request.getSession();
13 String noLoginPaths = config.getInitParameter("noLoginPaths"); //已获取filterconfig对象,可以获取其中属性
14
15 String charset = config.getInitParameter("charset");
16 if(charset==null){
17 charset = "UTF-8";
18 }
19 request.setCharacterEncoding(charset);
20
21 if(noLoginPaths!=null){
22 String[] strArray = noLoginPaths.split(";"); //对属性的值分割。分别放行
23 for (int i = 0; i < strArray.length; i++) {
24 if(strArray[i]==null || "".equals(strArray[i]))continue;
25 if(request.getRequestURI().indexOf(strArray[i])!=-1 ){
26 arg2.doFilter(arg0, arg1);
27 return;
28 }
29 }
30 }
31 if(session.getAttribute("username")!=null){
32 arg2.doFilter(arg0, arg1);
33 }else{
34 response.sendRedirect("login.jsp");
35 }
36 }
37 @Override
38 public void init(FilterConfig arg0) throws ServletException {
39 config = arg0; //在初始化时把次对象赋值
40 }
41
42 }
//执行顺序:最先初始化init(),然后dofilter函数 最后destory()
这里,这个登录案例就完成了。
那【request -> filter1 -> filter2 ->filter3 -> …. -> request resource。】是什么呢
就是一个url-partten对应了多个filter,一个url,被拦截了好几次。
复制代码
1 <filter>
2 <filter-name>FirstFilter</filter-name>
3 <filter-class>com.imooc.filter.FirstFilter</filter-class>
4 </filter>
5 <filter>
6 <filter-name>SecondFilter</filter-name>
7 <filter-class>com.imooc.filter.SecondFilter</filter-class>
8 </filter>
9
10 <filter-mapping>
11 <filter-name>FirstFilter</filter-name>
12 <url-pattern>/index.jsp</url-pattern>
13 </filter-mapping>
14 <filter-mapping>
15 <filter-name>SecondFilter</filter-name>
16 <url-pattern>/index.jsp</url-pattern>
17 </filter-mapping>
这样就存在一个过滤器链,按xml中顺序执行。
index.jsp
1 <body>
2 This is my JSP page. <br>
3 <%
4 System.out.println("到了index.jsp");
5 %>
6 </body>
FirstFilter.java
1 public class FirstFilter implements Filter {
2
3 @Override
4 public void destroy() {
5 System.out.println("destroy---FirstFilter");
6 }
7
8 @Override
9 public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
10 System.out.println("start----doFilter--FirstFilter");
11 chain.doFilter(request, response);
12 HttpServletRequest req =(HttpServletRequest) request;
13 HttpServletResponse response2 =(HttpServletResponse) response;
19 System.out.println("end------doFilter--FirstFilter");
20 }
21
22 @Override
23 public void init(FilterConfig filterConfig) throws ServletException {
24 System.out.println("init----FirstFilter");
25 }
26
27
28 }
SecondFilter.java
1 public class SecondFilter implements Filter {
2
3 @Override
4 public void destroy() {
5 System.out.println("destroy-----SecondFilter");
6 }
7
8 @Override
9 public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
10 System.out.println("start---doFilter--SecondFilter");
11 chain.doFilter(request, response);
12 System.out.println("end---doFilter--SecondFilter");
13 }
14
15 @Override
16 public void init(FilterConfig filterConfig) throws ServletException {
17
18 System.out.println("init-----SecondFilter");
19 }
20
21 }
过滤器链具体的执行顺序:
在运行项目时,两个filter类都执行了init()初始化,控制台输出:
init—–FirstFilter
init—–SecondFilter
当访问index.jsp时,进入FirstFilter.java的dofilter函数,控制台会依次输出:
start—doFilter–FirstFilter
start—doFilter–SecondFilter
到了index.jsp
end—doFilter–FirstFilter
end—doFilter–SecondFilter
在dofilter函数中,先执行chain.doFilter(request, response);前的代码,然后下一个filter链的下一个filter,然后进入index.jsp。再继续依次执行chain.doFilter(request, response);后面的代码
Code1表示ilter(request, response);之前的代码Code2表示ilter(request, response);之后的代码。