拦截器

    拦截器是SpringMVC中的一个核心应用组件,主要用于处理多个Controller的共性问题.当我们的请求由DispatcherServlet派发到具体Controller之前首先要执行拦截器中一些相关方法,在这些方法中可以对请求进行相应预处理(例如权限检测,参数验证),这些方法可以决定对这个请求进行拦截还是放行。

1. 服务器与请求

1.1. 常见的WEB服务器

  1. Toncat服务器:我最常用的服务器,开放源代码的,运行servlet和JSP Web应用软件基于Java,比绝大多数的商业用的软件服务器要好。
  2. Apache服务器:使用广泛,开源代码,支持多个平台,相对其他服务器占的内存较大,是重量级产品。
  3. Microsoft IIS服务器:微软的,包括Web服务器,FTP服务器,NNTP服务器和SMTP服务器。需要购买。
  4. Nginx服务器:俄罗斯的一个站点开发的,相比于Apache服务器,Nginx占用内存小且较稳定。

1.2. 发送请求

    前端向服务器发送请求有2种,(1)通过浏览器发送请求,(2)进入到系统后,通过js发送请求。

1.3. 通过浏览器发送URL请求

(1)用户在浏览器上输入网址,包含协议和域名.
(2)浏览器获得IP地址,浏览器先找自身缓存是否有记录,没有的话再找操作系统缓存,再没有就请求本地DNS服务器帮忙,本地DNS再找不到再一层层往上,最终浏览器获得对应的IP地址。
(3)浏览器发送请求,浏览器根据HTTP协议,给对应IP地址的主机发送请求报文,默认端口为80,报文包括请求内容,浏览器信息,本地缓存,cookie等信息。
(4)web服务器接收请求,寻找文件,Tomcat服务器接收到请求,找对应的html文件
(5)返回数据,web服务器向浏览器反馈html文件,浏览器进行渲染,页面加载。

1.4. js文件发送请求

    在项目中,使用ajax向服务器发送请求,例如xxx.do。

2. 实现拦截器

拦截器需要实现 HandleInterceptor接口,或者继承HandlerInterceptorAdaptor抽象类;
HandlerInterceptor接口的三个方法:

  1. preHandle()
  2. postHandle()
  3. afterCompletion()

    inceptor的作用是,每次在前端向后台发送一个请求时do,后台都会先经过inceptor中的preHandle这个函数,判断这个请求是否满足要求(是否已经登录,是否是管理员),如果满足要求就返回true,系统会自动把这个do请求提交给controller对应的函数进行处理,controller中的函数调用完之后,再次进入Inception中的postHandle()和afterCompletion()方法中。否则preHandle返回false,不会提交这个请求,不会执行Controller中的函数,也不会执行之后的Inception中的postHandle()和afterCompletion()方法。
    服务器一启动,就会创建拦截器对象;拦截器是单例的,整个过程,拦截器只有一个实例对象。
    项目中需要实现一个登录系统,当用户没有登录时,不能访问系统的主页和其他页面,但是可以访问系统的登录界面,所以需要在mvc.xml中设置一下,不拦截登录的请求。

2.1. Controller

下面是用户登录的Controller实现,当前端访问login.jsp时,这时登录的请求不会被拦截器拦截,会执行login()方法,验证前端用户输入的用户名和密码是否正确,如果正确的话,将userName放入到session中,并返回给前端index,那么界面将会跳转到index.jsp,如果用户名或密码错误,那么返回给前端login,前端界面还是login.jsp。

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
42
43
44
45
46
47
48
49
50
51
52
53
54
@Controller
@RequestMapping("/user")
public class UserController {
@Autowired
private UserService userService;

public UserController() {
System.out.println(this.getClass().getName() + " 初始化");
}
@RequestMapping(value = "/login", method = RequestMethod.POST)
@ResponseBody
public String login(HttpServletRequest request, HttpSession session)
throws SQLException, IOException, NoSuchAlgorithmException, InvalidKeySpecException {
// userList存储了所有的用户
// 每个用户以HashMap的形式存储
// key分别是:"userName","password","salt"
ArrayList<HashMap<String, String>> userList = userService.getUserInfo();

String input_userName = request.getParameter("userName");
String input_password = request.getParameter("password");

PBKDF2Util pbkdf2Util = new PBKDF2Util();
// 判断当前用户输入的用户名和密码是否正确
boolean flag = false;
for (int i = 0; i < userList.size(); i++) {
HashMap<String, String> oneUser = userList.get(i);
String actual_userName = oneUser.get("userName");
String actual_password = oneUser.get("password");
String salt = oneUser.get("salt");
boolean password_match = pbkdf2Util.authenticate(input_password, actual_password, salt);
if (input_userName.equals(actual_userName) && password_match) {
flag = true;
break;
}
} // for
if (flag) {
session.setAttribute("user", input_userName);
return "{\"status\": 0, \"url\": \"index\"}";
} else {
request.setAttribute("msg", "用户名或密码错误");
return "{\"status\": 1, \"url\": \"login\", \"msg\": \"用户名或密码错误\"}";
}

}

@RequestMapping("/logout")
@ResponseBody
public String logout(HttpSession session) throws IOException {
// System.out.println("进入到logout()方法中");
// 清除session的数据
session.invalidate();
return "{\"status\": 0, \"url\": \"login\"}";
}
}

2.2. Interceptor

在interceptor中会拦截URL请求,如果session中的用户名为空会重定向到login.jsp。但在在做项目时遇见一个问题,拦截器只能拦截js中的ajax发来的URL请求,不能拦截浏览器发送的URL请求。也就是说如果用户在浏览器中输入index.jsp,不会经过拦截器,如果是js中的ajax发送的请求,会经过拦截器。如果用户没有登录直接在浏览器中输入index.jsp,这时页面依然可以进入到index.jsp,这说明拦截器没有起作用。为了应对这一情况,有三种解决方案:
(1)把判断用户是否登录的代码写到了jsp中,在jsp中写java代码需要加上<%%>,在这里判断session中的用户名,如果为空的话,直接重定向到login.jsp,这样用户在未登录的情况下,在浏览器上输入index.jsp,页面不会跳转到index.jsp中,还是在login.jsp中。
(2)把所有的jsp文件放在WEB-INF文件里,这样用户是直接不能访问WEB-INF文件下的jsp文件的。spring mvc的理念也是通过controller里的@RequestMapping来请求相关jsp页面,而非用户直接访问jsp页面。也就是说,jsp页面的访问需要通过controller来进行一次请求,因为会拦截对controller的请求,所以也就相当于拦截了jsp页面。如果要做登陆拦截,只需要把登陆页面不拦截,其余页面拦截进行是否登陆的验证即可。
(3)jsp如果不放在WEB-INF文件下,spring mvc是无法拦截的,这种情况下需要用最原始的servlet的Filter接口。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<%
//实现登录检查,如果用户没有登录,重定向到登录界面
//这段代码要加载所有需要验证页面里,使用
//<%@include file="/jsp/navigation.jsp"把登录验证加载其余jsp中
Object userName = "";
if (session == null) {
response.sendRedirect(request.getContextPath() + "/jsp/login.jsp");
return;
} else {
userName = session.getAttribute("user");
if (userName == null) {
response.sendRedirect(request.getContextPath() + "/jsp/login.jsp");
return;
} else {
userName = userName.toString();
}
}
%>
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
42
43
44
45
46
47
48
49
public class LoginInterceptor implements HandlerInterceptor {
// 步骤1
// 在前端发出一个url(xxx.do)请求时,先执行这个方法,判断当前用户是否为空
// 如果用户已经登录,则返回true,否则返回false
// 只有当该函数返回true时,才会调用controller中对应的函数,
// 返回false不用调用controller中的函数
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object arg2) throws Exception {
String path = request.getContextPath() + "/jsp/login.jsp";
HttpSession session = request.getSession(false);
if (session == null || !request.isRequestedSessionIdValid()) {
response.sendRedirect(path);
return false;
}

// 获取登录用户信息
String user = session.getAttribute("user").toString();
if (user == null) {
response.sendRedirect(path);
return false;
}
return true;

}

// 步骤2
// 当preHandle返回true,调用controller中的函数之后,会执行该函数
// 当preHandle返回false,不会执行该函数
@Override
public void postHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, ModelAndView arg3)
throws Exception {
// System.out.println("拦截后...");
// System.out.println("进入到LoginInterceptor的postHandle()方法中");

}


// 步骤3
// 当preHandle返回true,调用controller中的函数之后,执行完postHandle,会调用该函数
// 当preHandle返回false,不会执行该函数
@Override
public void afterCompletion(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, Exception arg3)
throws Exception {
// System.out.println("页面渲染后...");
// System.out.println("进入到LoginInterceptor的afterCompletion()方法中");

}

}

2.3. mvc配置文件

在mvc.xml配置文件中,需要对拦截器进行配置,因为login请求不需要拦截,所以把这个请求排除,这样当前端访问login.jsp页面时,就会显示出登录的界面。

1
2
3
4
5
6
7
8
9
10
<mvc:interceptors>
<mvc:interceptor>
<!-- 拦截以任意字符结尾的路径 ,匹配所有的路径 -->
<!--/**表示拦截所有的url及其子路径 -->
<mvc:mapping path="/**" />
<!-- 登录不进行拦截 -->
<mvc:exclude-mapping path="/**/*login*" />
<bean class="com.hz.EQbigdata.interceptor.LoginInterceptor"></bean>
</mvc:interceptor>
</mvc:interceptors>

页面加载的顺序:
前端输入一个网址,相当于发出一个url,比如querywda.jsp。首先拦截器拦截这个url,判断是否合法,如果合法,会交给controller处理,处理完之后才会显示querywda的界面,调用相应的querywda.js。如果不合法,就应该在inception就把这个请求拦截下来,重定向到login,这样querywda的界面也不会加载出来

打赏
0%