对于Java Web中的Filter和Interceptor的理解

网友投稿 255 2022-06-15


写在前面:因为部门项目中有用户登录验证方面的需求,故而学习了一下相关的验证技术,本文仅是作者个人学习的心得,由于水平有限,如有错误之处还请指出、见谅。

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

xmlns:xsi="http://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">

loginValidation

filter.LoginValidation

redirectPath

/index.jsp

disableloginValidation

N

logonString

/index.jsp

loginValidation

/*

web.xml中标签被用来配置Filter的初始化时使用的参数,其中标签表示参数的名字,可以是自己定义的任何名字,标签表示对应的初始化参数的值。上面的初始化参数中, redirectPath 定义了当验证不成功时页面重定向的的路径, logonString 定义了拦截器拦截的指定URL。 标签定义了拦截器的拦截模式,在 标签定义了拦截模式,上面的 /* 表示拦截所有。它和之前定义的指定拦截的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" %>

用户登陆界面

用户名:
密 码:

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

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" %>

登录成功!

欢迎!

配置好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.

*

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.

*

Note: 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.

*

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.

*

Note: 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.

*

Note: Will only be called if this interceptor's {@code preHandle}

* method has successfully completed and returned {@code true}!

*

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.

*

Note: 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

xmlns:xsi="http://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">

contextConfigLocation

classpath:databaseAccess.xml,classpath:service.xml

org.springframework.web.context.ContextLoaderListener

CharacterEncodingFilter

org.springframework.web.filter.CharacterEncodingFilter

encoding

utf-8

CharacterEncodingFilter

/*

dispatcherServlet

org.springframework.web.servlet.DispatcherServlet

contextConfigLocation

classpath:dispatcherServlet.xml

1

dispatcherServlet

/

涉及到HandlerInterceptor的配置文件 dispatcherServlet.xml

xmlns:xsi="http://w3.org/2001/XMLSchema-instance"

xmlns:context="http://springframework.org/schema/context"

xmlns:mvc="http://springframework.org/schema/mvc"

xsi:schemaLocation="http://springframework.org/schema/beans

http://springframework.org/schema/beans/spring-beans.xsd

http://springframework.org/schema/context

http://springframework.org/schema/context/spring-context.xsd

http://springframework.org/schema/mvc

http://springframework.org/schema/mvc/spring-mvc.xsd">

配置HandlerInterceptor有两种方式,一种是如上面所示,这张方式配置的HandlerInterceptor可以指定具体的拦截路径,另外一种方式是直接在 中使用标签进行配置: 按照这种方式配置的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是一致的。

来自:http://jianshu.com/p/39c0cfe25997


版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:iOS 圆角箭头矩形 提示框(ios15.5)
下一篇:如何备份导出 iOS 微信聊天数据库(怎么导出备份)
相关文章

 发表评论

暂时没有评论,来抢沙发吧~