Spring 学习小记(十五)

67 篇文章 0 订阅
Spring MVC 拦截器
 
本小记学习目标
  1. 拦截器的认识
  2. 拦截器的配置
  3. 拦截器的执行流程介绍
 
一、拦截器的认识
    Spring MVC的拦截器Interceptor与Servlet中的过滤器Filter类似,它的主要作用就是用于拦截用户的请求并做出相应的处理(验证权限、记录请求日志、判断用户否登录……)。
    在Spring MVC中使用拦截器需要对拦截器进行定义和配置。
    
   定义
       1. 通过实现HandlerInterceptor接口或者继承HandlerInterceptor接口的实现类
       2. 通过实现WebRequestInterceptor接口或者继承WebRequestInterceptor接口的实现类
    关于HandlerInterceptor接口中定义了三个方法
        preHandle:这个方法在控制器处理请求之前执行,它的返回值是一个boolean值,返回true则继续后续的操作,返回false则中断后续操作
        postHandle:这个方法在控制器处理请求之后,但在解析视图之前执行,在这个方法中可以对请求域中的模型和视图做进一步的修改
        afterCompletion:这个方法在控制器处理请求方法执行完成之后执行,也就是在视图渲染结束后,通常用来实现一些资源的清理、记录日志等工作
 
    配置
        为了让拦截器生效需要在Spring MVC的配置文件中进行配置
    <!-- 拦截器配置 -->
    <mvc:conterceptors>
        <!-- 全局拦截器配置 -->
        <bean class="com.xiaoie.interceptor.GlobalInterceptor"/>
        <mvc:interceptor>
            <!-- 配置拦截器的作用路径 -->
            <mvc:mapping path="/**"/>
            <!-- 配置不需要拦截的路径 -->
            <mvc:exclude-mapping path=""/>
            <bean class="com.xiaoxie.interceptor.Interceptor1"/>
        </mvc:interceptor>
        <mvc:interceptor>
            <!-- 配置拦截器的作用路径 -->
            <mvc:mapping path="/test"/>
            <bean class="com.xiaoxie.interceptor.Interceptor2"/>
        </mvc:interceptor>
    </mvc:conterceptors>
 
<mvc:interceptors>元素用来配置一组拦截器,它的子元素<bean>定义的是全局拦截器,所有请求都会拦截。<mvc:interceptor>则定义了指定路径的拦截器,其子元素<mvc:mapping>用来配置拦截器作用的路径,路径在它的属性path中定义,上面的/**表示拦截所有路径,/test表示拦截所有以/test结尾的路径。如果需要排除不需要拦截的路径需要在<mvc:exclude-mapping>子元素进行配置
注意:<mvc:interceptor>下的子元素需要按顺序进行配置
 
二、拦截器的执行流程介绍
假设在配置文件中只定义了一个拦截器,程序执行概要过程如下:
 
新增一个Maven的war工程
在pom.xml中添加如下依赖
< dependencies >
       <!-- context -->
       < dependency >
             < groupId >org.springframework </ groupId >
             < artifactId >spring-context </ artifactId >
             < version >5.0.2.RELEASE </ version >
       </ dependency >
    <!-- Spring MVC -->
    < dependency >
       < groupId >org.springframework </ groupId >
       < artifactId >spring- webmvc </ artifactId >
       < version >5.0.2.RELEASE </ version >
    </ dependency >
   
    <!-- Spring web -->
    < dependency >
       < groupId >org.springframework </ groupId >
       < artifactId >spring-web </ artifactId >
       < version >5.0.2.RELEASE </ version >
       </ dependency >
   
    <!-- commons-logging -->
    < dependency >
       < groupId >commons-logging </ groupId >
       < artifactId >commons-logging </ artifactId >
       < version >1.2 </ version >
    </ dependency >
   
    <!-- spring- aop -->
    < dependency >
       < groupId >org.springframework </ groupId >
       < artifactId >spring- aop </ artifactId >
       < version >5.0.2.RELEASE </ version >
    </ dependency >
   
       < dependency >
       < groupId >javax.servlet </ groupId >
       < artifactId > jstl </ artifactId >
       < version >1.2 </ version >
       </ dependency >
  </ dependencies>
 
新增一controller的实现类 com.xiaoxie.controller.InterceptorController
package com.xiaoxie.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class InterceptorController {
       //定义一个logger,用来记录日志
       private static final Log logger = LogFactory. getLog(InterceptorController. class);
       @RequestMapping( "/test")
       public String test(Model model) {
             logger.info( "============处理用户请求============");
             model.addAttribute( "message", "OK");
             return "test";
      }
}
 
添加拦截器的实现类com.xiaoxie.interceptor.TestInterceptor
package com.xiaoxie.interceptor;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import com.xiaoxie.controller.InterceptorController;
public class TestInterceptor implements HandlerInterceptor{
      
       //定义一个logger,用来记录日志
       private static final Log logger = LogFactory. getLog(InterceptorController. class);
       //在HandlerInterInterceptor的接口中有三个 defalut 方法我们需要重写一下以做测试
       @Override
       public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
                   throws Exception {
             logger.info( "==========在处理请求前执行方法:preHandle=========");
             logger.info( "~~~~~~~~~~~~~~~检查用户权限~~~~~~~~~~~~~~~");
             //这里返回true则继续,否则会中断后续操作
             return true;
      }
       @Override
       public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
                  ModelAndView modelAndView) throws Exception {
            
            Map<String, Object> model = modelAndView.getModel();
            Set<Entry<String, Object>> entrySet = model.entrySet();
             logger.info( "==========在处请求后视图返回前执行:postHandle=========");
             for (Map.Entry<String, Object> entry : entrySet) {
                   logger.info( entry.getKey() + "====>" + entry.getValue());
            }
             modelAndView.addObject( "message", "OKOK!");
      }
       @Override
       public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
                   throws Exception {
             logger.info( "==========在视图渲染结束之后执行:afterCompletion=========");
      }
}
 
添加springmvc的配置文件springmvc.xml,在其中添加的内容如下
<!-- spring自动扫描的包定义 -->
       < context:component-scan base-package= "com.xiaoxie.controller" />
      
       <!-- 配置视图解析器 -->
       < bean class= "org.springframework.web.servlet.view.InternalResourceViewResolver" id= "internalResourceViewResolver" >
             <!-- 前缀 -->
             < property name= " prefix" value= "/WEB-INF/jsp/" />
             <!-- 后缀 -->
             < property name= " suffix" value= ".jsp" />
       </ bean >
      
       <!-- 定义一个全局拦截器 -->
       < mvc:interceptors >
             < bean class= "com.xiaoxie.interceptor.TestInterceptor" ></ bean >
       </ mvc:interceptors >
 
在web.xml中新增对前端控制器的配置
<!-- 前端控制器 -->
       < servlet >
             < servlet-name >springDispatcherServlet </ servlet-name >
             < servlet-class >org.springframework.web.servlet.DispatcherServlet </ servlet-class >
             < init-param >
                   < param-name >contextConfigLocation </ param-name >
                   < param-value >classpath:springmvc.xml </ param-value >
             </ init-param >
             < load-on-startup >1 </ load-on-startup >
       </ servlet >
       <!-- Map all requests to the DispatcherServlet for handling -->
       < servlet-mapping >
             < servlet-name >springDispatcherServlet </ servlet-name >
             < url-pattern >/ </ url-pattern >
       </ servlet-mapping >
      
       <!-- 配置解决 psot 请求中文乱码问题 -->
       < 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 >
             < init-param >
                   < param-name >forceEncoding </ param-name >
                   < param-value >true </ param-value >
             </ init-param >
       </ filter >
       < filter-mapping >
             < filter-name >CharacterEncodingFilter </ filter-name >
             < url-pattern >/* </ url-pattern >
       </ filter-mapping >
 
定义一个jsp页面来展示视图/WEB-INF/jsp/test.jsp,在这个页面中直接展示model绑定的内容
${message }
 
访问工程的/test请求可以看到页面test.jsp展示的内容为OKOK!
在控制台我们可以看到打印的信息如下:
信息: ==========在处理请求前执行方法:preHandle=========
12月 29, 2020 9:25:52 下午 com.xiaoxie.controller.InterceptorController preHandle
信息: ~~~~~~~~~~~~~~~检查用户权限~~~~~~~~~~~~~~~
12月 29, 2020 9:25:52 下午 com.xiaoxie.controller.InterceptorController test
信息: ============处理用户请求============
12月 29, 2020 9:25:52 下午 com.xiaoxie.controller.InterceptorController postHandle
信息: ==========在处请求后视图返回前执行:postHandle=========
12月 29, 2020 9:25:52 下午 com.xiaoxie.controller.InterceptorController postHandle
信息: message====>OK
12月 29, 2020 9:25:53 下午 com.xiaoxie.controller.InterceptorController afterCompletion
信息: ==========在视图渲染结束之后执行:afterCompletion=========
 
以上为单个拦截器的执行流程,当存在多个拦截器的时候(在实际工程中常见),它们的执行是如何的呢?
preHandle方法是按照配置文件中的配置顺序进行执行的,postHandle方法和afterCompletion则是按照配置的相反顺序执行的
 
模拟用户登录权限验证实例(实际工程中一般也是用拦截器来做用户权限的验证操作)
 
操作过程:
1.用户通过浏览器 请求登录,携带用户名、密码请求参数
2.如果验证用户名、密码成功则跳转到main.jsp页面
3.如果验证用户名、密码不成功则转发到login.jsp
4.当用户点击主页面中的“退出”链接时回到主页面
 
新增一个实体类User,com.xiaoxie.pojo.User
package com.xiaoxie.pojo;
public class User {
       private String uname;
       private String upwd;
      
      
       public String getUname() {
             return uname;
      }
       public void setUname(String uname) {
             this. uname = uname;
      }
       public String getUpwd() {
             return upwd;
      }
       public void setUpwd(String upwd) {
             this. upwd = upwd;
      }
}
 
新增UserController实现类,com.xiaoie.controller.UserController
package com.xiaoxie.controller;
import javax.servlet.http.HttpSession;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import com.xiaoxie.pojo.User;
@ Controller
public class UserController {
       private static final Log logger = LogFactory. getLog(UserController. class);
       private static final String MAIN = "main";
       private static final String LOGIN = "login";
      
       @RequestMapping( "/tologin")
       public String toLogin(){
             return "login";
      }
      
       @RequestMapping( "/login")
       public String login(User user,Model model,HttpSession session) {
             logger.debug( "请求信息:uname=" + user.getUname() + ",upwd=" + user.getUpwd() );
             //对用户进行判断
             logger.debug( "对用户信息进行判断");
             if( "xiaoxie".equals( user.getUname()) && "123456".equals( user.getUpwd())) {
                   //判断成功,把用户信息绑定到session中
                   session.setAttribute( "user", user);
                   //重定向到主页面
                   return "redirect:" + MAIN;
            }
             model.addAttribute( "msg", "用户名或密码不正确!");
             return LOGIN;
      }
      
       @RequestMapping( "/main")
       public String toMain() {
             return MAIN;
      }
      
       @RequestMapping( "/logout")
       public String logout(HttpSession session) {
             session.invalidate();
             return LOGIN;
      }
}
 
新增一个拦截器实现类,com.xiaoxie.interceptor.LoginInterceptor,在这个方法中只对preHandle接口默认方法进行重写
package com.xiaoxie.interceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.springframework.web.servlet.HandlerInterceptor;
public class LoginInterceptor implements HandlerInterceptor{
       @Override
       public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
                   throws Exception {
             //获取请求的URI
            String requestURI = request.getRequestURI();
             //login.jsp或/ tologin 不需要处理
             if( requestURI.indexOf( "/tologin")>=0 || requestURI.indexOf( "/login")>=0) {
                   return true;
            }
            HttpSession session = request.getSession();
            Object user = session.getAttribute( "user");
             if( user != null)
                   return true;
             //没有登录或且不是登录页面,转发到登录页面去
             request.setAttribute( "msg", "还没有登录,请先登录!");
             request.getRequestDispatcher( "/WEB-INF/jsp/login.jsp").forward( request, response);
             return false;
      }
}
 
在Spring mvc的配置文件中配置这个拦截器
<!-- 定义一个全局拦截器 -->
       < mvc:interceptors >
             < bean class= "com.xiaoxie.interceptor.LoginInterceptor" ></ bean >
       </ mvc:interceptors >
 
新增视图index.jsp(在WebContent目录下),login.jsp(/WEB-INF/jsp目录下),main.jsp(/WEB-INF/jsp目录下)
index.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 >Insert title here </ title >
</ head >
< body >
       < a href= "${pageContext.request.contextPath } /tologin" >登录 </ a >
</ body >
</ html >
 
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 >Insert title here </ title >
</ head >
< body >
      ${msg }
       < form action= "${pageContext.request.contextPath } /login" method= "post" >
            用户名: < input type= "text" name= "uname" />< br />
            密    码: < input type= "password" name= "upwd" />< br />
             < input type= "submit" value= "登录" >
       </ form >
</ body >
</ html >
 
main.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 >Insert title here </ title >
</ head >
< body >
      欢迎用户:${user.uname }
       < a href= "${pageContext.request.contextPath } /logout" >注销 </ a >
</ body >
</ html >
 
如上则完成了基本的用户权限校验。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值