springboot之拦截器
当我们的系统存在页面需要登录才能使用时,可以使用拦截器去做登录判断。
登录和主页
为了模拟使用场景,我写了一个包含登录和主页的Controller,如下
@Controller
public class MyController {
// 登录页
@GetMapping("/login")
public String loginPage() {
return "login";
}
// 登录
@PostMapping("/login")
public String login(HttpSession session, @RequestParam String username, @RequestParam String password) {
if (StringUtils.hasLength(username) && StringUtils.hasLength(password)) {
// 若输入了用户名和密码,则向session中添加用户名
session.setAttribute("user", username);
// 重定向到首页
return "redirect:/";
} else {
// 否则返回登录页
return "login";
}
}
// 首页
@GetMapping("/")
public String indexPage() {
return "index";
}
}
创建拦截器
拦截器需要实现接口HandlerInterceptor
,其包含三个方法,分别是
preHandle
:视图解析器执行前执行,当返回false
时不执行视图解析器postHandle
:视图解析器执行后执行afterCompletion
:页面渲染完成后执行,当preHandle
为true
时,无论后面视图解析器是否真正执行,都会执行afterCompletion
登录的拦截器如下
public class LoginInterceptor implements HandlerInterceptor {
// 视图解析器执行前
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
HttpSession session = request.getSession();
String user = (String) session.getAttribute("user");// 获取session中的用户
if (StringUtils.hasLength(user)) {// 如果用户存在,则放行
return true;// 放行
}
// 若没有登录,则拦截,重定向到登录页
response.sendRedirect("/login");
return false;// 拦截
}
// 视图解析器执行后
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
}
// 页面渲染完成后
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
throws Exception {
}
}
注册拦截器
我们需要写一个配置类来将我们的拦截器加入到springboot中,并指定拦截器生效的范围,因为配置类要定制SpringMVC的功能,所以需要实现WebMvcConfigurer
接口,重写addInterceptors
方法,如下
@Configuration
public class MyConfigurer implements WebMvcConfigurer {
// 添加自定义拦截器
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LoginInterceptor())
.addPathPatterns("/**")// 对所有请求进行拦截,包括静态资源访问
.excludePathPatterns("/login"); // 排除登录页
}
}
/**
:表示当前路径及其子路径下的所有路径/*
:表示当前路径下的所有路径,不包括子路径
注意,当拦截器拦截/**
路径时,会拦截静态资源的访问,所以需要将静态资源的访问路径加入到排除路径中。我们可以设置静态资源访问前缀,再将该前缀加入到排除路径中即可。这里我没用到静态资源,所以没加。
拦截器执行过程
- 根据当前请求,找到
HandlerExecutionChain
【包括可以处理请求的handler以及handler的所有拦截器】 - 先来顺序执行所有拦截器的
preHandle
方法- 如果当前拦截器
preHandle
返回为true,则执行下一个拦截器的preHandle
- 如果当前拦截器返回为 false,直接倒序执行所有已经执行了的拦截器的
afterCompletion
- 如果当前拦截器
- 如果任何一个拦截器返回 false,直接跳出不执行目标方法
- 所有拦截器都返回True,执行目标方法
- 倒序执行所有拦截器的
postHandle
方法 - 前面的步骤有任何异常都会直接倒序触发
afterCompletion
- 页面成功渲染完成以后,也会倒序触发
afterCompletion