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是一致的。

最新文章

  1. ORA-00257 archiver error 处理思路
  2. 大话immutable.js
  3. 字符串—strcpy
  4. NEC学习 ---- 布局 -两列定宽
  5. 用PHP获取系统时间时,时间比当前时间少8个小时
  6. 布局神器display:table-cell
  7. Java 实现阶乘算法
  8. js截取指定字节长度的字符串
  9. Find K most Frequent items in array
  10. html5上传文件并监听进度
  11. office2013 win 32bit (含激活工具)
  12. 如何将磁盘从GPT格式转换成MBR
  13. AWK调用SHELL,并将变量传递给SHELL
  14. 服务启动错误1053,一例解决方案(给用户添加NetworkService权限)
  15. 如何关闭IE浏览器在生成原型时候的安全警告
  16. celery最佳实践
  17. 201521123053《Java程序设计》第四周总结
  18. JS对json操作的扩展
  19. 给artDialog插件增加动画效果
  20. tr 命令

热门文章

  1. bzoj 1704: [Usaco2007 Mar]Face The Right Way 自动转身机【贪心+差分】
  2. 牛客OI周赛2-提高组
  3. ROS-USB摄像头
  4. 自动构造词法分析器的步骤——正规式转换为最小化DFA
  5. DP BestCoder Round #50 (div.2) 1003 The mook jong
  6. python批量删除文件夹
  7. SQL函数-汉字首字母查询
  8. blender_(uv应用)................http://digitalman.blog.163.com/blog/static/23874605620174172058299/
  9. phpExcel导出excel打不开问题
  10. CSS 类名的问题