Spring入门-Interceptor基本使用
SpringMVC提供了拦截器Interceptor,可以用于验证用户登录,它跟过滤器是有区别的,拦截器是Spring提供的,而过滤器是Servlet提供的。
使用拦截器的条件
使用拦截器前面需要进行配置,包括导包、web.xml中配置DispatcherServlet,Spring启动读取文件中配置组件扫描、注解驱动、视图解析器和拦截器。其他就是需要写一个控制器用来进行请求分发处理,还有自定义拦截器。
自定义拦截器
自定义拦截器需要实现HandlerInterceptor接口,实现接口定义的方法。
1 package Interceptors; 2 3 import javax.servlet.http.HttpServletRequest; 4 import javax.servlet.http.HttpServletResponse; 5 6 import org.springframework.web.servlet.HandlerInterceptor; 7 import org.springframework.web.servlet.ModelAndView; 8 /** 9 * 拦截器类需要实现HandlerInterceptor接口 10 * @author yangchaolin 11 * 12 */ 13 public class SomeInterceptor implements HandlerInterceptor{ 14 /** 15 * DispatcherServlet在收到请求后,会先调用preHandler方法,如果该方法的返回值为true,则继续向后调用Controller的方法 16 * 如果返回值是false,则中断请求 17 * 18 * DispatcherServlet,拦截器、Controller会共享同一个request,response 19 * handler:Controller的方法对象,利用了java反射机制,后面了解学习 20 */ 21 public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) 22 throws Exception { 23 System.out.println("拦截器的preHandle方法执行了"); 24 return true; 25 } 26 /** 27 * 是Controller处理完后,在将ModelAndView返回给前端控制器DispatcherServlet之前,执行的方法 28 * 可以在该方法里,修改ModelAndView的处理结果 29 */ 30 public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, 31 ModelAndView modelAndView) throws Exception { 32 System.out.println("拦截器的postHandle方法执行了"); 33 } 34 /** 35 * 最后执行 36 * ex:是处理器Controller所抛出的异常 37 * 可以写一个拦截器专门处理Controller抛出的异常 38 */ 39 public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) 40 throws Exception { 41 System.out.println("拦截器的afterCompletion方法执行了"); 42 } 43 }
Controller
写一个Controller来测试拦截器方法的执行顺序,里面为了测试多级目录写了两个方法。
1 package Controller; 2 3 import org.springframework.stereotype.Controller; 4 import org.springframework.web.bind.annotation.RequestMapping; 5 6 @Controller 7 public class HelloController { 8 @RequestMapping("/hello.do") 9 public String hello() { 10 System.out.println("控制器的hello()方法执行了"); 11 return "hello"; 12 13 } 14 /** 15 * 如果路径加一个demo,然而拦截器mapping还是/*的话,将不会拦截 16 * 如果想实现各种路径的拦截,不论几层都能实现拦截效果的话,需要将mapping修改为/** 17 * @return 18 */ 19 @RequestMapping("/demo/hello.do") 20 public String hello1() { 21 System.out.println("hello1()"); 22 return "hello"; 23 } 24 }
配置拦截器
在Spring启动默认读取文件applicationContext.xml文件中添加拦截器配置,这里只设置了一个拦截器,可以配置多个拦截器,执行顺序从上往下拦截。
1 <!-- 设置拦截器Interceptor --> 2 <!-- 如果想要拦截所有的请求,path应该写成/** --> 3 <mvc:interceptors> 4 <!-- 第一个拦截器 --> 5 <mvc:interceptor> 6 <mvc:mapping path="/**"/> 7 <bean class="Interceptors.SomeInterceptor"></bean> 8 </mvc:interceptor> 9 <!-- 后面可以配置多个拦截器,拦截顺序从上到下 --> 10 </mvc:interceptors>
运行结果,只展示hello.do请求,多级目录/demo/hello.do也可以拦截。
控制台输出情况
可以看出来拦截器方法的执行顺序
(1)DispatcherServlet接受到请求后,首先会根据HandlerMapping配置请求的结果(这里使用注解@RequestMapping来完成),查看是否有对应请求Mapping
(2)如果没有匹配路径不再往下执行,如果有请求匹配的话,会先执行拦截器方法preHandle,返回true则继续执行Controller的方法
(3)Controller执行完逻辑准备将结果(String或者ModelAndView)返回给前端控制器前,先执行postHandle方法,如果有异常,接下来在afterCompletion方法中进行处理,最后将结果返回给前端控制器
拦截器用于登录验证
接下来使用拦截器,在前面登录案例的基础上,添加一个用户登录验证,即登录成功的才跳转到后面页面,否则返回到登录页面,前期配置参考博文。
(1)JSP准备
登录页面、主页,sub页,主页用于登录验证,主页直接登录,sub页直接登录。
1 <%@page pageEncoding="utf-8"%> 2 <%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> 3 <!doctype html> 4 <html> 5 <head> 6 <title>欢迎登录Color Filter-JavaScript登录验证</title> 7 <meta charset="utf-8"> 8 <link rel="stylesheet" type="text/css" href="css/login.css" /> 9 <!-- <script type="text/javascript" src="script/login.js"></script> --> 10 </head> 11 <body> 12 <!--logo区+段落区--> 13 <div class="logo"> 14 <img src="image/LogoCF.jpg"> 15 <p id="logoDesc">Sign in to Color Filter</p> 16 </div> 17 <!--主体登录区--> 18 <div class="main"> 19 <div id="login"> 20 <!--5行2列--> 21 <!--表单用于输入登录信息,提交给服务器 --> 22 <!-- 23 onsubmit是表单提交事件,在点击提交按钮时触发。触发时先调用onsubmit内的方法,若该方法返回true则会自动提交表单 24 返回false则不提交,此处声明的方法可以起到拦截提交的作用,避免账号名和密码都不对时也能提交到服务器 25 onsubmit="return ((check_username()+check_pwd())==2)" 26 --> 27 <form action="logincheck.do" method="get"> 28 <table> 29 <tr> 30 <td colspan="2" style="text-align: left;text-indent: 29px">Username or email address</td> 31 </tr> 32 <tr> 33 <!-- 增加切换光标确认用户名格式是否正确 --> 34 <td colspan="2"><input type="text" name="user" id="username" onblur="check_username();" value="${username }"></td> 35 </tr> 36 <tr> 37 <td style="text-align: left;text-indent: 29px;width:15%">Password</td> 38 <td style="text-align: left;text-indent: 0px;width:85%"><a href="#">Forget password?</a></td> 39 </tr> 40 <tr> 41 <td colspan="2" style="width: 345px"><input type="password" name="pwd" id="pwd" onblur="check_pwd();" value="${password }"></td> 42 </tr> 43 <tr> 44 <td colspan="2" style="text-align: left;text-indent: 29px">Security Code</td> 45 </tr> 46 <tr> 47 <td style="padding-left:32px;width:60%;text-align:left;"><input type="text" name="valicode" id="valicode"/></td> 48 <!-- <td style="padding-top:3px;width:40%;text-align:right;padding-right:32px"><img alt="验证码" src="createIMG.do" onclick="this.setAttribute('src','createIMG.do?x='+Math.random())"></td> --> 49 <!-- 添加登录失败提示 --> 50 <td>${login_fail}</td> 51 </tr> 52 <tr> 53 <td colspan="2" id="buttontd"><input type="submit" name="btn" value="Sign in" id="button"></td> 54 </tr> 55 </table> 56 </form> 57 </div> 58 <div id="regist"> 59 <p style="text-align: center;">New to Color Filter? <a href="#">Create an account.</a></p> 60 </div> 61 </div> 62 <!--版权、隐私、法律相关--> 63 <div class="foot"> 64 <p><a href="#">Terms</a> <a href="#">Privacy</a> <a href="#">Security</a> Contact Color Filter</p> 65 </div> 66 </body> 67 68 </html>
1 <%@page pageEncoding="utf-8" contentType="text/html; charset=utf-8"%> 2 <h1>欢迎登陆</h1><br/> 3 <!-- 得到服务器返回的user参数,采用el表达式获取 --> 4 ${user } 5 <!-- 拦截器测试 --> 6 <div> 7 <form action="toSubPage.do" method="post"> 8 <a href="javascript:document.forms[0].submit();">点我继续访问</a> 9 </form> 10 </div>
1 <%@page pageEncoding="utf-8" contentType="text/html; charset=utf-8"%> 2 <h1>欢迎来到subPage</h1>
(2)自定义拦截器类及拦截器配置
拦截器类
1 package com.boe.interceptor; 2 3 import javax.servlet.http.HttpServletRequest; 4 import javax.servlet.http.HttpServletResponse; 5 6 import org.springframework.web.servlet.HandlerInterceptor; 7 import org.springframework.web.servlet.ModelAndView; 8 9 /** 10 * 配置自定义拦截器 11 * @author yangchaolin 12 */ 13 public class LoginInterceptor implements HandlerInterceptor{ 14 //在一次请求中,DispatcherServlet、拦截器和Controller共用一个request和response 15 public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) 16 throws Exception { 17 //进行登录验证拦截,除了toLogin.do,logincheck.do都进行拦截 18 Object obj=request.getSession().getAttribute("loginData"); 19 if(obj==null) { 20 //返回登录页面 21 System.out.println("没有登录,进行拦截"); 22 //重定向,使用不带"/",相对路径 23 response.sendRedirect("toLogin.do"); 24 return false; 25 }else { 26 System.out.println("登录成功,继续访问"); 27 return true; 28 } 29 } 30 31 public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, 32 ModelAndView modelAndView) throws Exception { 33 // TODO Auto-generated method stub 34 35 } 36 37 public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) 38 throws Exception { 39 // TODO Auto-generated method stub 40 41 } 42 }
applicationContext.xml文件中配置拦截器
1 <!-- 添加拦截器,不拦截进入登录页面,登录验证 --> 2 <mvc:interceptors> 3 <mvc:interceptor> 4 <mvc:mapping path="/**"/> 5 <mvc:exclude-mapping path="/toLogin.do"/> 6 <mvc:exclude-mapping path="/logincheck.do"/> 7 <bean class="com.boe.interceptor.LoginInterceptor"></bean> 8 </mvc:interceptor> 9 </mvc:interceptors>
(3)修改LoginController控制器方法,登录成功后将用户信息保存到session中。
1 package com.boe.controller; 2 3 import javax.annotation.Resource; 4 import javax.servlet.http.HttpSession; 5 6 import org.springframework.stereotype.Controller; 7 import org.springframework.ui.ModelMap; 8 import org.springframework.web.bind.annotation.RequestMapping; 9 10 import com.boe.entity.Admin; 11 import com.boe.entity.userInfo; 12 import com.boe.exception.ApplicationException; 13 import com.boe.service.LoginService; 14 15 @Controller 16 public class mainController { 17 @Resource(name="loginServiceImpl") 18 private LoginService service; 19 20 //去到登录页面 21 @RequestMapping("/toLogin.do") 22 public String toLogin() { 23 System.out.println("去到登录页面"); 24 return "login"; 25 } 26 27 //验证登录 28 @RequestMapping("/logincheck.do") 29 public String login(userInfo data,ModelMap mm,HttpSession session) { 30 System.out.println("登录验证"); 31 System.out.println("用户名为:"+data.getUser()); 32 System.out.println("密码为:"+data.getPwd()); 33 //处理业务层异常 34 try { 35 Admin admin=service.checkLogin(data.getUser(), data.getPwd()); 36 if(admin!=null) { 37 System.out.println("登录成功"); 38 mm.addAttribute("user",data.getUser()); 39 //登录成功后,将admin信息存入session 40 session.setAttribute("loginData", admin); 41 } 42 }catch(Exception e) { 43 //应用错误,即用户填写错误 44 if(e instanceof ApplicationException) { 45 mm.addAttribute("login_fail", e.getMessage()); 46 mm.addAttribute("username",data.getUser()); 47 mm.addAttribute("password", data.getPwd()); 48 return "login"; 49 } 50 //系统错误 51 return "loginNG"; 52 } 53 return "loginOK"; 54 } 55 56 //直接进入主页,拦截器测试 57 @RequestMapping("/loginOK.do") 58 public String toMainPage() { 59 System.out.println("准备进入loginOK主页"); 60 return "loginOK"; 61 } 62 //直接进入sub页,拦截器测试 63 @RequestMapping("/toSubPage.do") 64 public String toSubPage() { 65 System.out.println("准备进入subPage副页"); 66 return "subPage"; 67 } 68 }
(4)测试不登录的情况下,进入loginOK页面,发现被拦截了,并提示没有登录。
控制台情况,显示执行了自定义拦截器的preHandle方法,并返回false,不再执行controller里的方法,进行重定向到登录页面。
(5)测试登录的情况下,访问loginOK.do和toSubPage.do 。
控制台情况,可以看出验证登录是没有拦截的,登录成功后进入主页,然后点击链接时就会被拦截到,因为有登录所以拦截器的preHandle方法返回true并打印出了"登录成功,继续访问",后进入LoginController继续处理逻辑,打印出了"准备进入subPage副页",并返回String结果给前端控制器,让前端控制器分发视图解析器最后完成副页展示。
总结
拦截器可以用于登录验证,但是其属于Spring,如果前端更换框架则不再生效,而过滤器是servlet范畴,配置了过滤器无论怎么更换都会过滤,相比较来说过滤器范围更广。
参考博文:
(1)https://www.cnblogs.com/youngchaolin/p/11354307.html springMVC完成登录页面
(2)https://www.cnblogs.com/youngchaolin/p/10549020.html 过滤器和拦截器
(3)https://blog.csdn.net/koflance/article/details/79635240 URI、URL和URN