Java Web学习总结(29)——Java Web中的Filter和Interceptor比较
1. 背景
在设计web应用的时候,用户登录/注册是必不可少的功能,对用户登录信息进行验证的方法也是多种多样,大致可以认为如下模式:前端验证+后台验证。根据笔者的经验,一般会在前端进行一些例如是否输入数据、输入的数据的格式是否正确等一系列的验证,在后台会查询数据库进行验证。
一般在后台进行验证的时候,都会选择使用Servlet的Filter作为拦截器,本文主要介绍Servlet的Filter,然后再拿它跟Spring MVC的HnadlerInterceptor进行对比。
2. Filter
2.1 什么是Filter
Servlet作为Java Web的基础,它的一个比较核心也被广泛应用的功能就是Filter,又叫拦截器。顾名思义,拦截器就是起到拦截作用的。一般情况下,用户从客户端发出请求到服务器后,整个的流程是:
HttpRequest ----> Filter ----> Servlet ----> Controller/Action/... ----> Filter ----> HttpResponse
根据上面的流程可以看出,Filter的作用就是在用户请求到达Servlet之前,进行拦截。在拦截到用户的请求后,我们可以实现一些自定义的业务逻辑,例如之前说到的对用户登录信息进行验证。Filter还可以在服务器响应到达客户端之前对响应的数据进行修改,本文主要介绍第一个功能。
2.2 Filter的工作原理
Filter跟Servlet一样都是由服务器负责创建和销毁的,在web应用程序启动时,服务器会根据应用程序的web.xml文件中的配置信息调用 public void init(FilterConfig
 filterConfig) throws ServletException 方法来初始化Filter,在web应用程序被移除或者是服务器关闭时,会调用 public void destroy() 来销毁Filter。在一个应用程序中一个Filter只会被创建和销毁一次,在进行完初始化之后,Filter中声明了 public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException,
 ServletException 方法,用来实现一些需要在拦截完成之后的业务逻辑。
注意到上面的 doFilter() 方法的参数中,有 chain 这个参数,它是传递过来的拦截链对象,里面包含了用户定义的一系列的拦截器,这些拦截器根据其在web.xml中定义的顺序依次被执行。当用户的信息验证通过或者当前拦截器不起作用时,我们可以执行
 chain.doFilter() 方法来跳过当前拦截器来执行拦截器链中的下一个拦截器。
2.3 自己实现Filter
自己实现Filter时,需要继承接口 javax.servlet.Filter 并且实现相关的方法。
2.3.1所用到的工具:
IDE: IntelliJ IDEA
构建工具:gradle
本地服务器:Tomcat
2.3.2 具体代码
build.gradle
group 'xiangang.wei'
version '1.0-SNAPSHOT' apply plugin: 'java'
apply plugin: 'war' sourceCompatibility = 1.8 repositories {
jcenter()
mavenCentral()
}
dependencies {
testCompile group: 'junit', name: 'junit', version: '4.11'
// servlet-api
compile group: 'javax.servlet', name: 'servlet-api', version: '2.5'
}
web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" version="3.0">
<filter>
<filter-name>loginValidation</filter-name>
<filter-class>filter.LoginValidation</filter-class>
<init-param>
<param-name>redirectPath</param-name>
<param-value>/index.jsp</param-value>
</init-param>
<init-param>
<param-name>disableloginValidation</param-name>
<param-value>N</param-value>
</init-param>
<init-param>
<param-name>logonString</param-name>
<param-value>/index.jsp</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>loginValidation</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
web.xml中<init-param>标签被用来配置Filter的初始化时使用的参数,其中<param-name>标签表示参数的名字,可以是自己定义的任何名字,<param-value>标签表示对应的初始化参数的值。上面的初始化参数中,
 redirectPath 定义了当验证不成功时页面重定向的的路径, logonString 定义了拦截器拦截的指定URL。 <filter-mapping> 标签定义了拦截器的拦截模式,在 <url-pattern> 标签定义了拦截模式,上面的 /* 表示拦截所有。它和之前定义的指定拦截的URL标签结合起来使用。
index.jsp
<%--
Created by IntelliJ IDEA.
User: xiangang
Date: 2016/11/21
Time: 下午3:42
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>用户登陆界面</title>
<style type="text/css">
div{
margin: auto;
border: gray 1px solid;
width: 70%;
}
</style>
</head>
<body>
<div>
<form action="success.jsp" method="post">
<table>
<tr>
<td>用户名:<input type="text" name="name" /></td>
</tr>
<tr>
<td>密 码:<input type="password" name="password" /></td>
</tr>
<tr>
<td><input type="submit" value="提交" /></td>
<td><input type="reset" value="重置" /></td>
</tr>
</table>
</form>
</div>
</body>
</html>
Filter对应的实现类:
LoginValidation.java
package filter;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException; /** *
Created by xiangang on 2016/11/21.
*/ public class LoginValidation implements Filter{
public FilterConfig config;
public static boolean isContains(String url, String[] regx){
boolean flag = false;
for (int i = 0;i<regx.length;i++){
if (url.indexOf(regx[i])!=-1){
flag = true;
return flag;
}
}
return flag;
} @Override
public void init(FilterConfig filterConfig) throws ServletException {
this.config = filterConfig;
} @Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest httpServletRequest= (HttpServletRequest)request;
HttpServletResponse httpServletResponse = (HttpServletResponse)response;
String name = httpServletRequest.getParameter("name");
String password = httpServletRequest.getParameter("password");
String redirectPath=httpServletRequest.getContextPath()+config.getInitParameter("redirectPath");
String logonString = config.getInitParameter("logonString");
String[] logonList = logonString.split(";"); if (isContains(httpServletRequest.getRequestURI(),logonList)){
chain.doFilter(request,response);
return;
}
if ("Y".equals(config.getInitParameter("disableloginValidation"))){
chain.doFilter(request,response);
return;
}
if ("root".equals(name) && "admin".equals(password)){
chain.doFilter(request,response);
return;
}else{
httpServletResponse.sendRedirect(redirectPath);
return;
}
} @Override public void destroy() {
this.config = null;
}
}
登录成功之后的页面:
success.jsp
<%--
Created by IntelliJ IDEA.
User: xiangang
Date: 2016/11/21
Time: 下午3:56
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>登录成功!</title>
</head>
<body>
欢迎!
</body>
</html>
配置好Tomcat之后,开启应用程序:


登录失败时不会跳转,仍然会停留在原页面。
3. Interceptor
之前提到的Filter是Servlet层面的拦截器,在许多的Java Web框架中,都实现了自己的拦截器Interceptor。例如Struts2中的Interceptor、Spring
 MVC中的HandlerInterceptor等。相比于Filter,框架中的Interceptor的产生作用的时间和位置不一样,下面描述了应用了Spring MVC中的HandlerInterceptor的web请求流程:
HttpRequest ----> DispactherServlet ----> HandlerInterceptor ---->Controller----> HandlerInterceptor
 ----> HttpResponse
两者的主要区别在于Filter起作用的时机是在请求到达Servlet之前,二HandlerInterceptor其作用的时机是在DispactherServlet接收到用户请求完成请求到相应的Handler映射之后。虽然都先于在具体的业务逻辑执行,但是还是存在一些差异。Filter面对的是所有的请求,而HandlerInterceptor是面对具体的Controller。Filter总是先于HandlerInterceptor发挥作用,在Filter中甚至可以中断请求,从而使它无法到达相应的Servlet。而且两者的配置也不一样,Filter是在web.xml中进行配置,HandlerInterceptor是在具体的applicationContext.xml中进行配置。
3.1 HandlerInterceptor的工作原理
分析源码,发现HandlerInterceptor接口中声明了如下几个方法:
public interface HandlerInterceptor {  
/**
* Intercept the execution of a handler. Called after HandlerMapping determined
* an appropriate handler object, but before HandlerAdapter invokes the handler.
* <p>DispatcherServlet processes a handler in an execution chain, consisting
* of any number of interceptors, with the handler itself at the end.
* With this method, each interceptor can decide to abort the execution chain,
* typically sending a HTTP error or writing a custom response.
* <p><strong>Note:</strong> special considerations apply for asynchronous
* request processing. For more details see
* {@link org.springframework.web.servlet.AsyncHandlerInterceptor}.
* @param request current HTTP request
* @param response current HTTP response
* @param handler chosen handler to execute, for type and/or instance evaluation
* @return {@code true} if the execution chain should proceed with the
* next interceptor or the handler itself. Else, DispatcherServlet assumes
* that this interceptor has already dealt with the response itself.
* @throws Exception in case of errors
*/   
boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;  
/**
* Intercept the execution of a handler. Called after HandlerAdapter actually
* invoked the handler, but before the DispatcherServlet renders the view.
* Can expose additional model objects to the view via the given ModelAndView.
* <p>DispatcherServlet processes a handler in an execution chain, consisting
* of any number of interceptors, with the handler itself at the end.
* With this method, each interceptor can post-process an execution,
* getting applied in inverse order of the execution chain.
* <p><strong>Note:</strong> special considerations apply for asynchronous    * request processing. For more details see
* {@link org.springframework.web.servlet.AsyncHandlerInterceptor}.
* @param request current HTTP request
* @param response current HTTP response
* @param handler handler (or {@link HandlerMethod}) that started asynchronous
* execution, for type and/or instance examination
* @param modelAndView the {@code ModelAndView} that the handler returned
* (can also be {@code null})
* @throws Exception in case of errors
*/   
void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception;   
/**
* Callback after completion of request processing, that is, after rendering
* the view. Will be called on any outcome of handler execution, thus allows
* for proper resource cleanup.
* <p>Note: Will only be called if this interceptor's {@code preHandle}
* method has successfully completed and returned {@code true}!
* <p>As with the {@code postHandle} method, the method will be invoked on each
* interceptor in the chain in reverse order, so the first interceptor will be
* the last to be invoked.
* <p><strong>Note:</strong> special considerations apply for asynchronous
* request processing. For more details see
* {@link org.springframework.web.servlet.AsyncHandlerInterceptor}.
* @param request current HTTP request
* @param response current HTTP response
* @param handler handler (or {@link HandlerMethod}) that started asynchronous
* execution, for type and/or instance examination
* @param ex exception thrown on handler execution, if any
* @throws Exception in case of errors
*/   
void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception;
}
这三个方法分别会在具体的HandlerController方法执行之前,执行成功之后,和执行完成之后被执行。
3.2 自己实现HandlerInterceptor
Spring MVC框架采用了适配器的开发模式,使用一个抽象的类 HandlerInterceptorAdapter 实现 HandlerInterceptor 接口,这样当我们需要自己实现HandlerInterceptor时,我们可以继承
 HandlerInterceptorAdapter 这样我们就不用全部实现这三个方法,而可以选择性的实现自己需要的方法。
3.2.1所用到的工具:
IDE: IntelliJ IDEA
构建工具:gradle
本地服务器:Tomcat
3.2.2具体的代码:
build.gradle
group 'xiangang.wei'
version '1.0-SNAPSHOT'
apply plugin: 'java'
apply plugin: 'war'source Compatibility = 1.8 repositories {
jcenter()
mavenCentral()
} dependencies {
testCompile group: 'junit', name: 'junit', version: '4.11'
// servlet-api
compile group: 'javax.servlet', name: 'servlet-api', version: '2.5'
//spring相关
compile group: 'org.springframework', name: 'spring-webmvc', version: '4.3.3.RELEASE'
compile group: 'org.springframework', name: 'spring-orm', version: '4.3.3.RELEASE'
compile group: 'org.springframework', name: 'spring-aspects', version: '4.3.3.RELEASE'
compile group: 'org.springframework.security', name: 'spring-security-config', version: '3.2.0.RELEASE'
compile group: 'org.springframework.security', name: 'spring-security-taglibs', version: '3.2.0.RELEASE'
compile 'org.springframework.security:spring-security-web:3.2.0.RELEASE' //hibernate相关
compile 'org.hibernate:hibernate-core:4.3.6.Final'
//mysql
compile group: 'mysql', name: 'mysql-connector-java', version: '5.1.39'
//springData
compile group: 'org.springframework.data', name: 'spring-data-jpa', version: '1.10.3.RELEASE'
// https://mvnrepository.com/artifact/log4j/log4j日志
compile group: 'log4j', name: 'log4j', version: '1.2.17'
//json解析相关
compile group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: '2.5.4'
compile group: 'com.fasterxml.jackson.core', name: 'jackson-core', version: '2.5.4'
}
web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" version="3.0">
<!--框架默认帮我们配置好了ApplicationContext的实现类(org.springframework.web.context.support.XmlWebApplicationContext),不需要自己手动配置--> <!--配置ApplicationContext需要加载的配置文件-->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:databaseAccess.xml,classpath:service.xml</param-value>
</context-param> <!--ApplicationContext的加载和关闭-->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!--配置字符过滤器,防止出现中文乱码-->
<filter>
<filter-name>CharacterEncodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>utf-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>CharacterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping> <!--配置Spring MVC的前置控制器-->
<servlet>
<servlet-name>dispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:dispatcherServlet.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
涉及到HandlerInterceptor的配置文件 dispatcherServlet.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd"> <!--开启注解-->
<mvc:annotation-driven/> <!--添加需要扫描的包-->
<context:component-scan base-package="ims" use-default-filters="false">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan> <!--添加HandlerInterceptor-->
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/user/**"/>
<bean class="ims.handlerInterceptor.LoginInterceptor"/>
</mvc:interceptor>
<mvc:interceptor>
<mvc:mapping path="/register/**"/>
<bean class="ims.handlerInterceptor.RegisterInterceptor"/>
</mvc:interceptor>
</mvc:interceptors> <!--添加试图解析器-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/views/"/>
<property name="suffix" value=".jsp"/>
</bean> </beans>
配置HandlerInterceptor有两种方式,一种是如上面所示,这张方式配置的HandlerInterceptor可以指定具体的拦截路径,另外一种方式是直接在 <mvc:interceptors>
 中使用<bean>标签进行配置: <bean class="ims.handlerInterceptor.LoginInterceptor"/> 按照这种方式配置的HandlerInterceptor会对所有的路径进行拦截。
具体的Interceptor实现类:
LoginInterceptor.java
package ims.handlerInterceptor;
import ims.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.stereotype.Service;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession; /**
* Created by xiangang on 16/11/17.
*/ public class LoginInterceptor extends HandlerInterceptorAdapter { @Autowired
private UserService userService; @Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
boolean flag = true;
HttpSession session = request.getSession();
if (session.getAttribute("user") == null) {
String userName = request.getParameter("userName");
String password = request.getParameter("password");
flag = userService.selectByUserName(userName, password);
if (flag){
session.setAttribute("user",userName);
}
}
if (!flag){
response.sendRedirect("index.jsp");
}
return flag;
}
}
具体的运行结果这里也就不再贴图了,实现的效果跟之前的Filter是一致的。
Java Web学习总结(29)——Java Web中的Filter和Interceptor比较的更多相关文章
- java web 学习 --第六天(Java三级考试)
		
第五天学习在这:http://www.cnblogs.com/tobecrazy/p/3458592.html session对象 当某个用户首次访问web应用系统时,jsp会自动创建出一个sessi ...
 - Java Web学习(一)Web基础
		
文章更新时间:2020/07/24 一.基本概念 web资源 Internet上供外界访问的Web资源分为两种: 静态web资源(如html 页面):指web页面中供人们浏览的数据始终是不变. 动态w ...
 - Java高精度学习第三弹——ACM中使用JAVA的详细介绍
		
Chapter I. Java的优缺点各种书上都有,这里只说说用Java做ACM-ICPC的特点: (1) 最明显的好处是,学会Java,可以参加Java Challenge . (2) 对于熟悉C/ ...
 - 我最推荐的一张Java后端学习路线图,Java工程师必备
		
前言 学习路线图往往是学习一样技术的入门指南.网上搜到的Java学习路线图也是一抓一大把. 今天我只选一张图,仅此一图,足以包罗Java后端技术的知识点.所谓不求最好,但求最全,学习Java后端的同学 ...
 - JAVA基础学习——1.0 Java概述
		
Java语言 SUN公司 1995年推出的高级编程语言 ■ 主要应用方向 Web开发和Android开发 ■ 主要特点 平台无关性:能运行于不同的平台上 安全性:去掉了指针操作,内存由操作 ...
 - [转]web.xml中servlet ,filter ,listener ,interceptor的作用与区别
		
原文链接:https://blog.csdn.net/netdevgirl/article/details/51483273 一.概念: 1.servlet:servlet是一种运行服务器端的java ...
 - Java基础学习笔记一 Java介绍
		
java语言概述 Java是sun公司开发的一门编程语言,目前被Oracle公司收购,编程语言就是用来编写软件的. Java的应用 开发QQ.迅雷程序(桌面应用软件) 淘宝.京东(互联网应用软件) 安 ...
 - Java基础学习笔记五 Java基础语法之面向对象
		
面向对象 理解什么是面向过程.面向对象 面向过程与面向对象都是我们编程中,编写程序的一种思维方式.面向过程的程序设计方式,是遇到一件事时,思考“我该怎么做”,然后一步步实现的过程.例如:公司打扫卫生( ...
 - 我的Java开发学习之旅------>Java 格式化类(java.util.Formatter)基本用法
		
本文参考: http://docs.oracle.com/javase/1.5.0/docs/api/java/util/Formatter.html http://www.blogjava.net/ ...
 
随机推荐
- setsockopt 设置socket 详细用法(转载)
			
转自:http://www.cppblog.com/killsound/archive/2009/01/16/72138.html 1.closesocket(一般不会立即关闭而经历TIME_WAIT ...
 - Android 在eclipse中没有出现AVD的解决方法(转载)
			
转自:http://frabbit2013.blog.51cto.com/1067958/1243549 本文主要介绍在系统中成功配置好Android开发环境(即SDK is ok and ADT o ...
 - contesthunter 6201 走廊泼水节【克鲁斯卡尔+并查集】
			
很有意思的题,所以还是截lyddalao的课件 #include<iostream> #include<cstdio> #include<algorithm> us ...
 - 计算几何值平面扫面poj2932 Coneology
			
Coneology Time Limit: 5000MS Memory Limit: 65536K Total Submissions: 4097 Accepted: 859 Descript ...
 - BFS(最短路+路径打印) POJ 3984 迷宫问题
			
题目传送门 /* BFS:额,这题的数据范围太小了.但是重点是最短路的求法和输出路径的写法. dir数组记录是当前点的上一个点是从哪个方向过来的,搜索+,那么回溯- */ /************* ...
 - 二分图最大匹配(匈牙利算法) POJ 3041 Asteroids
			
题目传送门 /* 题意:每次能消灭一行或一列的障碍物,要求最少的次数. 匈牙利算法:把行和列看做两个集合,当有障碍物连接时连一条边,问题转换为最小点覆盖数==二分图最大匹配数 趣味入门:http:// ...
 - E - A^B mod C (大数乘方取模)
			
Description Given A,B,C, You should quickly calculate the result of A^B mod C. (1<=A,B,C<2^63) ...
 - RabbitMQ三:Rabbit的安装
			
本章文章,摘自 园友 章为忠 的文章,查找了很多资料,他总结的最细,最全面,我就直接拿过来了 他的原文 http://www.cnblogs.com/zhangweizhong/p/5689209.h ...
 - php angular/think angular/php模版引擎
			
在thinphp5中发现一个好用的模版引擎—think-angular, 此模板引擎主要特点是 不需要额外的标签定义, 全部使用属性定义, 写好的模板文件在IDE格式化代码的时候很整洁, 因为套完的模 ...
 - python语言真正的奥义所在--对接32单片机
			
2018-02-2720:51:24 今天晚上注定我要玩一夜这个东西,太爽了,给力! 烧写固件成功, http://blog.csdn.net/Lingdongtianxia/article/deta ...