【SpringMVC】拦截器

拦截器

1、什么是拦截器

SpringMVC的处理器拦截器类似于Servlet开发中的过滤器Filter,用于对处理器进行预处理和后处理。开发者可以自己定义一些拦截器来实现特定的功能。

2、拦截器与过滤器

过滤器与拦截器的区别:拦截器是AOP思想的具体应用。

过滤器

  • servlet规范中的一部分,任何java web工程都可以使用
  • 在url-pattern中配置了/*之后,可以对所有要访问的资源进行拦截

拦截器

  • 使用了SpringMVC框架的工程才能使用
  • 拦截器只会拦截访问的控制器方法, 如果访问的是jsp/html/css/image/js是不会进行拦截的

3、通过AOP织入实现拦截器效果(体会拦截器与AOP的联系)

1、新建一个Moudule , springmvc-07-Interceptor , 添加web支持

2、配置web.xml 和 applicationContext.xml 文件

  • web.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<filter>
<filter-name>encoding</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>encoding</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
  • applicationContext.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
<?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" xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">

<!-- 注解驱动 JSON乱码配置 -->
<mvc:annotation-driven>
<mvc:message-converters register-defaults="true">
<bean class="org.springframework.http.converter.StringHttpMessageConverter">
<constructor-arg value="UTF-8"/>
</bean>
<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
<property name="objectMapper">
<bean class="org.springframework.http.converter.json.Jackson2ObjectMapperFactoryBean">
<property name="failOnEmptyBeans" value="false"/>
</bean>
</property>
</bean>
</mvc:message-converters>
</mvc:annotation-driven>

<!-- 自动扫描指定的包,下面所有注解类交给IOC容器管理-->
<context:component-scan base-package="indi.zhihuali.controller"/>

<!-- 静态资源过滤 -->
<mvc:default-servlet-handler/>

<!-- 视图解析器-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"
id="InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/"/>
<property name="suffix" value=".jsp"/>
</bean>
</beans>

3、前置日志类与后置日志类

1
2
3
4
5
public class BeforeLog implements MethodBeforeAdvice {
public void before(Method method, Object[] objects, Object o) throws Throwable {
System.out.println(o.getClass().getName()+"的"+method.getName()+"方法被执行了");
}
}
1
2
3
4
5
public class AfterLog implements AfterReturningAdvice {
public void afterReturning(Object o, Method method, Object[] objects, Object o1) throws Throwable {
System.out.println("执行了"+o1.getClass().getName()+"的"+method.getName()+"方法,返回值为:"+o);
}
}

4、通过AOP织入日志功能

1
2
3
4
5
6
7
8
<!--用配置文件的切点和前置、后置请求 测试aop(与过滤器原理相同)-->
<bean id="before" class="indi.zhihuali.log.BeforeLog"/>
<bean id="after" class="indi.zhihuali.log.AfterLog"/>
<aop:config>
<aop:pointcut id="pointcut" expression="execution(* indi.zhihuali.controller.HelloController.*(..))"/>
<aop:advisor advice-ref="before" pointcut-ref="pointcut"/>
<aop:advisor advice-ref="after" pointcut-ref="pointcut"/>
</aop:config>

4、编写一个拦截器

想要自定义拦截器,必须实现 HandlerInterceptor 接口。

1、新建一个Moudule , springmvc-07-Interceptor , 添加web支持

2、配置web.xml 和 springmvc-servlet.xml 文件

web.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<filter>
<filter-name>encoding</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>encoding</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>

applicationContext.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
<?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" xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">

<!-- 注解驱动 JSON乱码配置 -->
<mvc:annotation-driven>
<mvc:message-converters register-defaults="true">
<bean class="org.springframework.http.converter.StringHttpMessageConverter">
<constructor-arg value="UTF-8"/>
</bean>
<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
<property name="objectMapper">
<bean class="org.springframework.http.converter.json.Jackson2ObjectMapperFactoryBean">
<property name="failOnEmptyBeans" value="false"/>
</bean>
</property>
</bean>
</mvc:message-converters>
</mvc:annotation-driven>

<!-- 自动扫描指定的包,下面所有注解类交给IOC容器管理-->
<context:component-scan base-package="indi.zhihuali.controller"/>

<!-- 静态资源过滤 -->
<mvc:default-servlet-handler/>

<!-- 视图解析器-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"
id="InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/"/>
<property name="suffix" value=".jsp"/>
</bean>
</beans>

3、编写一个拦截器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class myInterceptor implements HandlerInterceptor {

// return true:执行下一个拦截器 放行
// return false:不执行下一个拦截器 拦截
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("===================执行前===================");
// 改为false的话 就不会去自己去跳到dispatcherservlet中执行对应方法了
return true;
}

// 拦截日志
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("===================执行后===================");

}

public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("===================清理===================");

}
}

4、applicationContext.xml中配置拦截器

1
2
3
4
5
6
7
8
9
10
11
<!--关于拦截器的配置-->
<mvc:interceptors>
   <mvc:interceptor>
       <!--/** 包括路径及其子路径-->
       <!--/admin/* 拦截的是/admin/add等等这种 , /admin/add/user不会被拦截-->
       <!--/admin/** 拦截的是/admin/下的所有-->
       <mvc:mapping path="/**"/>
       <!--bean配置的就是拦截器-->
       <bean class="com.kuang.interceptor.MyInterceptor"/>
   </mvc:interceptor>
</mvc:interceptors>

5、编写Controller接受请求

1
2
3
4
5
6
7
8
9
@RestController
public class HelloController {
// AOP具体应用:拦截器
@RequestMapping("/t1")
public String test(){
System.out.println("HelloController");
return "hello";
}
}

6、测试Controller请求

upload successful

upload successful

5、验证用户是否登陆(认证)

实现思路

1、有一个登陆页面,需要写一个controller访问页面。

2、登陆页面有一提交表单的动作。需要在controller中处理。判断用户名密码是否正确。如果正确,向session中写入用户信息。返回登陆成功。

3、拦截用户请求,判断用户是否登陆。如果用户已经登陆。放行, 如果用户未登陆,跳转到登陆页面

upload successful

测试:

  1. 在index.jsp中编写首页展示信息
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>$Title$</title>
</head>
<body>
<h1>
<a href="${pageContext.request.contextPath}/user/toLogin">跳转到登录页面</a>
</h1>
<h1>
<a href="${pageContext.request.contextPath}/user/main">首页</a>
</h1>
</body>
</html>

​ 2.编写登录页面login.jsp和首页main.jsp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>登录页</title>
</head>
<body>
<h1>登录:</h1>
<form action="${pageContext.request.contextPath}/user/login" method="post">
用户名:<input type="text" name="username">
密码:<input type="text" name="password">
<input type="submit" value="提交">
</form>
</body>
</html>
1
2
3
4
5
6
7
8
9
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>首页</title>
</head>
<body>
<h1>首页</h1>
</body>
</html>

3.编写Controller方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
@Controller
@RequestMapping("/user")
public class LoginController {
// 登录成功跳转
@RequestMapping("/login")
public String login(HttpSession session,String username, String password){
session.setAttribute("userInfo", username);
System.out.println(username);
return "main";
}

// 跳转到登录页面
@RequestMapping("/toLogin")
public String toLogin(){
return "login";
}

// 跳转到首页
@RequestMapping("/main")
public String main(HttpSession session){
System.out.println(session.getAttribute("userInfo"));
return "main";
}
}

4.编写拦截器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// 自定义拦截器需要实现拦截器接口
public class loginInterceptor implements HandlerInterceptor {
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

// 获取Session对象
HttpSession session = request.getSession();

// 为了避免用户在没有登录的情况下直接进入首页:
// 什么情况下可以放行:Session中有信息
if(session.getAttribute("userInfo")!=null){
return true;
}
// 或者 用户在请求登陆时 可以放行
if(request.getRequestURI().contains("toLogin")){
return true;
}
// 或者 用户在第一次提交登录时会自动放行(如果不提交就登陆的话 Session中没有信息 也不会放行)
if(request.getRequestURI().contains("login")){
return true;
}
else {
// 什么情况下没有登录:Session中没有信息 执行拦截
request.getRequestDispatcher("/WEB-INF/jsp/login.jsp").forward(request, response);
return false;
}
}
}

5.applicationContext中配置拦截器

1
2
3
4
5
6
7
<!--表示user请求下的所有子请求都会被拦截-->
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/user/**"/>
<bean class="indi.zhihuali.interceptor.loginInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>

6.测试运行

upload successful

第一次点击首页时 会跳转到登录界面

upload successful

登录后 返回并再次点击首页 会直接跳转到首页界面


注意:

这里有一个错误点,找了好久!
在定义cotroller类请求时,自作聪明觉得老师学的user不好,自己概括成了loginOperator
,然后会发现在用户第一次点击首页的时候也会被拦截器放行。
而与开始设想的完全不同

upload successful

通过在自定义的登录拦截器类中,对应的条件进行输出语句看到底是进入了哪个条件

upload successful

结果显示进入了tj3 也就是第一次提交登录那个请求中

**原因就是我的类请求为’/loginOperator’**也就是说 类中的任何请求都会被放行!!!!

改过类controller请求后 便可以跳转到登录页面

6.注销操作

main.jsp中添加用户名显示及注销操作

1
2
3
4
   <span>${username}</span>
<p>
<a href="${pageContext.request.contextPath}/user/logout">注销</a>
</p>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@RequestMapping("/login")
public String login(HttpSession session, String username, String password, Model model){
session.setAttribute("userInfo", username);
// 回显用户名
model.addAttribute("username", username);
return "main";
}
````
controller中添加logout请求
```java
@RequestMapping("/logout")
public String logout(HttpSession session){
session.removeAttribute("userInfo");
return "login";
}

测试:

upload successful

点击注销后

upload successful