Spring MVC之HandlerInterceptor拦截器

  拦截器是 Web 项目很重要和常用的功能,如对用户权限验证,判断用户是否已登录等。

  SpringMVC 中的拦截器通过实现HanderInterceptor接口来完成,或继承抽象类HandlerInterceptorAdapter,重写里面的方法来完成。

HanderInterceptor

  HanderInterceptor 接口中定义了三个方法,SpringMVC 通过这三个方法来对用户请求进行拦截处理。

  1. boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)

    本方法在请求处理之前执行,返回值是 Boolean 类型,返回值为 false 时,表示请求结束;返回 true 时就会继承调用下一个 interceptorpreHandle() 方法,如果是最后一个 Interceptor,就调用当前请求的 Controller 方法。SpringMVC 中的 Interceptor 是链式调用,即在一个应用中或一个请求中可以同时存在多个Interceptor, 调用的的顺序是根据声明的顺序依次执行;所以可以在该方法中对一些请求做一个预处理,或初始化操作。

    handler 参数是目标处理方法,是 HandlerMethod 类型,即处理请求的具体方法。可以通过该参数获取目标方法对象,再根据目标方法对象获取方法上的注解,再根据注解进行业务处理。

    1
    2
    3
    4
    5
    6
    HandlerMethod handler1 = (HandlerMethod) handler;
    Method method1 = handler1.getMethod();
    RequestMapping methodAnnotation = handler1.getMethodAnnotation(RequestMapping.class);
    if (null != methodAnnotation) {
    System.out.println("-----------------");
    }
  2. void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)
    preHandle()方法返回true;,请求被处理之后,也就是 Controller 方法被调用之后执行,但是在DispatcherServlet 进行视图返回渲染之前被调用。

    多个 Interceptor 时,该方法的调用顺序与 preHandle() 相反。

  3. void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
    preHandle() 方法返回true,整个请求结束之后,DispatcherServlet 进行视图返回渲染之后执行。该方法主要作用是进行资源清理。

  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
    29
    public interface HandlerInterceptor {

    /**
    * 拦截处理执行器
    * 在 HandlerMapping 确定适当的处理程序对象之后,但在 HandlerAdapter 调用处理程序之前。
    */
    default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
    throws Exception {

    return true;
    }

    /**
    * 实际上在HandlerAdapter之后调用执行,但在 DispatcherServlet 呈现视图之前。
    * 可以通过给定的ModelAndView将其他模型对象公开给视图。
    */
    default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
    @Nullable ModelAndView modelAndView) throws Exception {
    }

    /**
    * 完成请求处理后(即渲染视图之后)的回调。
    * 将在处理程序执行的任何结果上被调用,从而允许适当的资源清理。
    * 该方法将在每个链中的拦截器的顺序相反,因此第一个拦截器将是最后被调用。
    */
    default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,
    @Nullable Exception ex) throws Exception {
    }
    }

拦截器链调用顺序

实现示例

登录认证拦截器

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
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import com.anqi.entity.User;

/**
* 登录拦截器 登录login请求放行; 其它请求则判断是否已登录,未登录则跳转到登录界面
*
* @author Rocky
*
*/
public class LoginInterceptor implements HandlerInterceptor {

// 以下URI放行不拦截
private static final String[] IGNORE_URI = { "/loginForm", "/login", "/register" };

/**
* preHandle方法是进行拦截处理,在Controller处理之前调用 该方法返回true时才会继续往下执行,返回false则整个请求结束。
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object obj) throws Exception {
//默认用户没有登录
boolean flag = false;
//获取ServletPath
String servletPath = request.getServletPath();
//判断是否有需要放行的请求
for (String uri : IGNORE_URI) {
if (servletPath.contains(uri)) {
flag = true;
break;
}
}

if (!flag) {
// 1. 获取session中的用户
HttpSession session = request.getSession();
User user = (User) session.getAttribute("user");
// 2. 判断用户是否已登录
if (user == null || "".equals(user.toString())) {
request.setAttribute("msg", "请先登录");
request.getRequestDispatcher("loginForm").forward(request, response);

// response.sendRedirect("/admin/userLogin");
// resp.sendRedirect(req.getSession().getServletContext().getContextPath()+LOGIN_URL);
// req.getRequestDispatcher("/WEB-INF/jsp/admin-login.jsp").forward(req, resp);

return false;
} else {
flag = true;
}
}
return flag;
}


/**
* 该方法将在Controller方法调用之后执行,方法中可对ModelView进行操作
* 也只能在当前Interceptor的preHandle方法返回true时才会执行
*/
@Override
public void postHandle(HttpServletRequest req, HttpServletResponse resp, Object obj, ModelAndView exception)
throws Exception {
// TODO Auto-generated method stub

}

/**
* 该方法将在整个请求完成之后执行,主要作用是用于清理资源 也只能在当前Interceptor的preHandle方法返回true时才会执行
*/
@Override
public void afterCompletion(HttpServletRequest req, HttpServletResponse resp, Object obj, Exception exception)
throws Exception {
// TODO Auto-generated method stub

}


}

登录Token认证拦截器

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
55
56
57
58
59
60
61
62
public class AuthenticationInterceptor implements HandlerInterceptor {
@Autowired
UserService userService;
@Override
public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object object) throws Exception {
String token = httpServletRequest.getHeader("token");// 从 http 请求头中取出 token
// 如果不是映射到方法直接通过
if(!(object instanceof HandlerMethod)){
return true;
}
HandlerMethod handlerMethod=(HandlerMethod)object;
Method method=handlerMethod.getMethod();
//检查是否有passtoken注释,有则跳过认证
if (method.isAnnotationPresent(PassToken.class)) {
PassToken passToken = method.getAnnotation(PassToken.class);
if (passToken.required()) {
return true;
}
}
//检查有没有需要用户权限的注解
if (method.isAnnotationPresent(UserLoginToken.class)) {
UserLoginToken userLoginToken = method.getAnnotation(UserLoginToken.class);
if (userLoginToken.required()) {
// 执行认证
if (token == null) {
throw new RuntimeException("无token,请重新登录");
}
// 获取 token 中的 user id
String userId;
try {
userId = JWT.decode(token).getAudience().get(0);
} catch (JWTDecodeException j) {
throw new RuntimeException("401");
}
User user = userService.findUserById(userId);
if (user == null) {
throw new RuntimeException("用户不存在,请重新登录");
}
// 验证 token
JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256(user.getPassword())).build();
try {
jwtVerifier.verify(token);
} catch (JWTVerificationException e) {
throw new RuntimeException("401");
}
return true;
}
}
return true;
}

@Override
public void postHandle(HttpServletRequest httpServletRequest,
HttpServletResponse httpServletResponse,
Object o, ModelAndView modelAndView) throws Exception {

}
@Override
public void afterCompletion(HttpServletRequest httpServletRequest,
HttpServletResponse httpServletResponse,
Object o, Exception e) throws Exception {
}

装配拦截器

拦截/admin/开头的所有请求。

1
2
3
4
5
6
7
8
<!-- 配置sprignmvc 拦截器 -->
<mvc:interceptors>
<!-- 多个拦截器,顺序执行 -->
<mvc:interceptor>
<mvc:mapping path="/admin/**" />
<bean class="com.interceptor.LoginInterceptor"></bean>
</mvc:interceptor>
</mvc:interceptors>

Spring MVC之HandlerInterceptor拦截器

http://blog.gxitsky.com/2018/02/28/SpringMVC-13-interceptor/

作者

光星

发布于

2018-02-28

更新于

2022-06-17

许可协议

评论