怪谈SpringMVC中的拦截器

332 阅读4分钟

这是我参与更文挑战的第 20 天,活动详情查看

SpringMVC中的拦截器

有很多场景会使用到拦截器,比如登录认证、身份授权、统一异常处理、统一日志处理,使用拦截器很容易就能实现上述功能,

二、SpringMVC 拦截器简介以及简单使用

1、SpringMVC 拦截器简介

1)SpringMVC 拦截器(Interceptor)实现对每一个请求处理前后进行相关的业务处理,类似与 servlet 中的 Filter。

2)SpringMVC 中的 Interceptor 拦截请求是通过 HandlerInterceptor 来实现的。

3)在 SpringMVC 中定义一个 Interceptor 非常简单,主要有4种方式:

  • 实现 Spring 的 HandlerInterceptor 接口;(本章节案例使用此方式)
  • 继承实现了 HandlerInterceptor 接口的类,比如 Spring 已经提供的实现了HandlerInterceptor 接口的抽象类 HandlerInterceptorAdapter;
  • 实现 Spring 的 WebRequestInterceptor 接口;
  • 继承实现 WebRequestInterceptor 的类;

2、定义拦截器

本章节中所用拦截器都是用实现 HandlerIntercepter 接口方式,创建 LoginInterceptor 类,实现 HandlerInterceptor 接口并重写里面的三个方法。

public class LoginInterceptor implements HandlerInterceptor {
   //Handler执行前调用
   //应用场景:登录认证、身份授权
   //返回值为true则是放行,为false是不放行
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        return false;
    }
   //进入Handler开始执行,并且在返回ModelAndView之前调用
   //应用场景:对ModelAndView对象操作,可以把公共模型数据传到前台,可以统一指定视图
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {

    }
   //执行完Handler之后调用
   //应用场景:统一异常处理、统一日志处理
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {

    }
}

3、配置拦截器

SpringMVC 拦截器是绑定在 HandlerMapping 中的,即如果某个 HandlerMapping 中配置拦截,则该 HandlerMapping 映射成功的 Handler 会使用该拦截器。

1)针对单个 HandlerMapping 配置

只有通过该处理器映射器查找到的处理器,才能使用该拦截器。

如果现在有两个处理器映射器:其中一个设置了处理器拦截器,另外一个没有设置,如果通过第二个映射器查找到的处理器,是无法使用拦截器的。

<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping">
	<property name="interceptors">
		<list>
			<ref bean="interceptor" />
		</list>
	</property>
</bean>
<bean id="interceptor" class="xxx.xxx.xxx.interceptor.MyHandlerInterceptor" />

2)全局拦截器配置

SpringMVC 的全局拦截器配置,其实是把配置的拦截器注入到每个已初始化的HandlerMapping 中

<!-- 配置全局mapping的拦截器 -->
<mvc:interceptors>
     <!-- 公共拦截器可以拦截所有请求,而且可以有多个 -->
    <bean class="xxx.xxx.xxx.interceptor.MyHandlerInterceptor1" />
    <bean class="xxx.xxx.xxx.interceptor.MyHandlerInterceptor2" />
	<!-- 如果有多个拦截器,则按照顺序进行配置 -->
	<mvc:interceptor>
		<!-- /**表示所有URL和子URL路径 -->
		<mvc:mapping path="/test/**" />
                <!-- 特定请求的拦截器只能有一个 -->
		<bean class="xxx.xxx.xxx.interceptor.MyHandlerInterceptor3" />
	</mvc:interceptor>
</mvc:interceptors>

这里需要注意的是:如果有多个拦截器,那么配置在 springmvc.xml 中最上面的拦截器,拦截器优先级最高。

三、拦截器实现登录认证 demo

这里使用拦截器模拟登录认证,只是模拟一下登录认证的大体流程,并没有实现真正的认证,目的在于让读者了解拦截器的具体应用。

1、具体需求

  • 拦截器对访问的请求 URL 进行拦截校验;
  • 如果请求的 URL 是公开地址(无需登录就可以访问的 URL,具体指的就是保护 login 字段的请求 URL),采取放行。如果用户 session 存在,则放行;
  • 如果用户 session 中不存在,则跳转到登录页面。

2、登录 jsp 页面:login.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
         pageEncoding="UTF-8" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title>登录页面</title>
</head>
<body>
<form action="${pageContext.request.contextPath }/login" method="post">
    <table align="center" border="1" cellspacing="0">
        <tr>
            <td>用户名:<input type="text" name="username"/></td>
        </tr>
        <tr>
            <td>密 码:<input type="text" name="password"/></td>
        </tr>
        <tr>
            <td><input type="submit" value="登录"/></td>
        </tr>
    </table>
</form>
</body>
</html>

3、LoginController.java

@Controller
public class LoginController {

    /**
     * 显示登录页面
     * @return
     */
    @RequestMapping("/loginPage")
    public String loginPage() {
        return "login/login";
    }

    /**
     * 登录
     * @param session
     * @param username
     * @param password
     * @return
     */
    @RequestMapping("/login")
    public String login(HttpSession session, String username, String password) {
        // 把用户信息保存到session中
        session.setAttribute("username", username);
        // 重定向到商品列表页面
        return "redirect:/queryItem";
    }

    // 退出
    @RequestMapping("/logout")
    public String logout(HttpSession session) {
        //清空session
        session.invalidate();
        // 重定向到登录页面
        return "redirect:/loginPage";
    }
}

4、拦截器:LoginInterceptor.java

public class LoginInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 获取请求的URI
        String requestURI = request.getRequestURI();
        System.out.println(requestURI);
        // 1.如果请求的URL是公开地址(无需登录就可以访问的URL),采取放行。
        if (requestURI.indexOf("login") > -1) {
            return true;
        }
        // 2.如果用户session存在,则放行。
        Object username = request.getSession().getAttribute("username");
        if (username != null && !username.equals("")) {
            return true;
        }
        // 3.如果用户session中不存在,则跳转到登录页面。
        response.sendRedirect("/loginPage");
        return false;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {

    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {

    }
}

这里因为只是做登录认证,所以只用到了 preHandle 方法,逻辑还是比较清晰的,注释标注的也很明白。

5、拦截器配置:springmvc.xml

<!-- 配置全局mapping的拦截器 -->
<mvc:interceptors>
	<!-- 如果有多个拦截器,则按照顺序进行配置 -->
	<mvc:interceptor>
		<!-- /**表示所有URL和子URL路径 -->
		<mvc:mapping path="/**"/>
		<bean class="top.alanshelby.interceptor.LoginInterceptor"/>
	</mvc:interceptor>
</mvc:interceptors>

这样,一个简单的拦截器就完成了,还是很简单的吧。

四、小结

拦截器是日常开发中不可或缺的一种应用,这里最常用的实现拦截器的方式就是实现 HandlerInterceptor 接口,自己一定要手动实现一下简单的 demo,让自己真正了解拦截器的使用