二、项目文件结构
三、开发准备
Spring MVC 的相关文件放在实验楼的服务器中,请打开实验环境桌面上的 Xfce ,输入下面的代码获取:
wget http://labfile.oss.aliyuncs.com/courses/810/Spring-jars.zip
输入下面的命令解压 .zip 文件:
unzip Spring-jars.zip
四、实验步骤
4.1 拦截器原理
对于 Spring MVC 来说,拦截器的主要作用是拦截用户的请求并进行相应的处理,如权限验证等。
Interceptor 拦截器拦截请求通过实现 HandlerInterceptor 接口来完成。HandlerInterceptor 接口中有三个方法:
-
preHandle()
:该方法在请求处理之前被调用。它的返回值是一个 boolean 类型,若为 true,则继续调用后面的拦截器和目标方法,若为 false,则不会调用后面的拦截器和目标方法,表示请求结束。 -
postHandle()
:该方法只能在当前所属的 Interceptor 的 preHandle 方法返回值为 true 时才能调用,它在当前请求进行处理之后,即 Controller 方法被调用之后,视图被渲染之前执行。 -
afterCompletion()
:该方法只能在当前所属的 Interceptor 的 preHandle 方法返回值为 true 时才能调用,它在整个请求结束之后,即视图被渲染之后执行,其主要作用是进行资源清理。
注:在 Spring MVC 中,一个请求可以添加多个拦截器 Interceptor,每个 Interceptor 的调用会根据声明顺序依次执行。最先执行的是 preHandle 方法, postHandle 方法的被调用的方向和 preHandle 相反。
4.2 新建项目工程
在 Eclipse 里新建一个 Web 工程(Dynamic Web Project),命名为 InterceptorTest
。
注意勾选 “自动生成 web.xml” 这个选项。
将 /home/shiyanlou/Spring-jars/
路径下的全部 jar 包拷贝到项目的 WebContent/WEB-INF/lib/
目录下。
4.3 配置 web.xml 文件
修改 web.xml 文件内容如下:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
id="WebApp_ID" version="3.0">
<display-name>InterceptorTest</display-name>
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/springmvc-config.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>
<welcome-file-list>
<welcome-file>/WEB-INF/views/loginform.jsp</welcome-file>
</welcome-file-list>
</web-app>
4.4 springmvc-config.xml 文件
在 WebContent/WEB-INF/
目录下新建 Spring MVC 配置文件 springmvc-config.xml
,添加如下代码:
<?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"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.2.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.2.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.2.xsd">
<mvc:annotation-driven />
<context:annotation-config />
<context:component-scan base-package="com.shiyanlou.controller" />
<bean
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/views/"></property>
<property name="suffix" value=".jsp"></property>
</bean>
<!-- 配置拦截器 -->
<mvc:interceptors>
<mvc:interceptor>
<!-- 拦截所有请求 -->
<mvc:mapping path="/*" />
<!-- 该类是我们自定义的Interceptor -->
<bean class="com.shiyanlou.interceptor.LoginInterceptor" />
</mvc:interceptor>
</mvc:interceptors>
</beans>
4.5 Controller 类的实现
在包 com.shiyanlou.controller
下新建 Controller 类 LoginController.java
,具体解释注释已经给出,代码如下:
package com.shiyanlou.controller;
import javax.servlet.http.HttpSession;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class LoginController {
// 登录
@RequestMapping(value = "/login")
public String login(HttpSession session, String username, String password,
Model model) {
// 判断用户名和密码是否正确
if (username.equals("shiyanlou") && password.equals("123456")) {
session.setAttribute("username", username);
// 重定向到 test 请求
return "redirect:test";
} else {
model.addAttribute("message", "wrong user name or password!");
return "loginform";
}
}
// 登出
@RequestMapping(value = "/loginout")
public String loginout(HttpSession session) throws Exception {
session.invalidate();
return "loginform";
}
}
在上面的代码中,我们假设正确的用户名为 "shiyanlou",密码为 "123456"。
在包 com.shiyanlou.controller
下新建 Controller 类 TestController.java
,代码如下:
package com.shiyanlou.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class TestController {
@RequestMapping(value="/test")
public String test(Model model) throws Exception{
return "success";
}
}
test 方法的作用是如果登录成功,跳转到成功页面。
4.6 拦截器的实现
我们需要编写一个拦截器来验证用户是否登录。在包 com.shiyanlou.interceptor
下新建类 LoginInterceptor.java
,代码如下:
package com.shiyanlou.interceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
public class LoginInterceptor implements HandlerInterceptor {
// 不拦截 "/login" 请求
private static final String[] IGNORE_URI = { "/login" };
@Override
public void afterCompletion(HttpServletRequest request,
HttpServletResponse response, Object handler, Exception exception)
throws Exception {
System.out.println("This is afterCompletion!");
}
@Override
public void postHandle(HttpServletRequest request,
HttpServletResponse response, Object handler, ModelAndView mv)
throws Exception {
System.out.println("This is postHandle!");
}
// 该方法将在 Controller 处理前进行调用
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) throws Exception {
System.out.println("This is preHandle!");
// flag 表示是否登录
boolean flag = false;
// 获取请求的 URL
String url = request.getServletPath();
for (String s : IGNORE_URI) {
if (url.contains(s)) {
flag = true;
break;
}
}
if (!flag) {
// 获取 Session 并判断是否登录
String username = (String) request.getSession().getAttribute(
"username");
if (username == null) {
request.setAttribute("message", "Please log in first!");
// 如果未登录,进行拦截,跳转到登录界面
request.getRequestDispatcher("/WEB-INF/views/loginform.jsp")
.forward(request, response);
} else {
flag = true;
}
}
return flag;
}
}
4.7 JSP 页面
(1)loginform.jsp
在 WebContent/WEB-INF
目录下新建文件夹 views
,并在该路径下新建一个 JSP 页面命名为 loginform.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>log in page</title>
</head>
<body>
<h4>log in page</h4>
<form action="login" method="post">
<font color="red">${requestScope.message}</font>
<table>
<tr>
<td>name:<input type="text" name="username" />
</tr>
<tr>
<td>password:<input type="password" name="password" />
</tr>
<tr>
<td><input type="submit" value="login" />
</tr>
</table>
</form>
</body>
</html>
(2)success.jsp
在 WebContent/WEB-INF/views
目录下新建一个 JSP 页面命名为 success.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>success page</title>
</head>
<body>
welcome,${sessionScope.username},
<a href="loginout">sign out</a>
</body>
</html>
4.8 运行测试
注:由于在线环境的资源问题,这里启动 tomcat 比较慢,需要大家耐心等待几分钟。如果遇到 Tomcat 启动超时的问题,请按照下图的方法延长 Tomcat 的启动时间。
右击 InterceptorTest 工程,Run As->Run on Server,保持默认选项不变,点击 Finish,一会儿即可看到结果:
在浏览器中输入 URL http://localhost:8080/InterceptorTest/test
访问 test 请求
,会被拦截器拦截,由于没有登录,跳转到登录页面。
输入错误的用户名或密码,登录失败,停留在登录页面。
输入正确的用户名和密码,登录成功,跳转至成功页面。
点击登出
链接,退出到登录页面。
再次在浏览器中输入 URL http://localhost:8080/InterceptorTest/test
访问 test 请求
,同样被拦截,这是因为登出的已经清除了 Session 中的账号信息。