拦截器是 Web 项目很重要和常用的功能,如对用户权限验证,判断用户是否已登录等。
SpringMVC 中的拦截器通过实现HanderInterceptor
接口来完成,或继承抽象类HandlerInterceptorAdapter
,重写里面的方法来完成。
HanderInterceptor
HanderInterceptor 接口中定义了三个方法,SpringMVC 通过这三个方法来对用户请求进行拦截处理。
boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
本方法在请求处理之前执行,返回值是
Boolean
类型,返回值为false
时,表示请求结束;返回true
时就会继承调用下一个 interceptor 的 preHandle() 方法,如果是最后一个 Interceptor,就调用当前请求的 Controller 方法。SpringMVC 中的 Interceptor 是链式调用,即在一个应用中或一个请求中可以同时存在多个Interceptor, 调用的的顺序是根据声明的顺序依次执行;所以可以在该方法中对一些请求做一个预处理,或初始化操作。handler 参数是目标处理方法,是 HandlerMethod 类型,即处理请求的具体方法。可以通过该参数获取目标方法对象,再根据目标方法对象获取方法上的注解,再根据注解进行业务处理。
HandlerMethod handler1 = (HandlerMethod) handler; Method method1 = handler1.getMethod(); RequestMapping methodAnnotation = handler1.getMethodAnnotation(RequestMapping.class); if (null != methodAnnotation) { System.out.println("-----------------"); }
void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)
preHandle()
方法返回true
;,请求被处理之后,也就是 Controller 方法被调用之后执行,但是在DispatcherServlet 进行视图返回渲染之前被调用。多个 Interceptor 时,该方法的调用顺序与 preHandle() 相反。
void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
preHandle() 方法返回true
,整个请求结束之后,DispatcherServlet 进行视图返回渲染之后执行。该方法主要作用是进行资源清理。接口源码
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 { } }
拦截器链调用顺序
实现示例
登录认证拦截器
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认证拦截器
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/
开头的所有请求。
<!-- 配置sprignmvc 拦截器 -->
<mvc:interceptors>
<!-- 多个拦截器,顺序执行 -->
<mvc:interceptor>
<mvc:mapping path="/admin/**" />
<bean class="com.interceptor.LoginInterceptor"></bean>
</mvc:interceptor>
</mvc:interceptors>
注意:本文归作者所有,未经作者允许,不得转载