在 Spring MVC 框架中, 拦截器作为一种机制, 用于对请求进行拦截. 拦截器可以在请求进入处理器之前、处理器返回处理之后、视图渲染之前等各个环节进行拦截.
拦截器通常用于实现一下功能 :
也就是说, 当需要对多个请求进行相同或者类似处理时, 可以使用拦截器来统一处理, 从而避免冗余的代码和低效的维护.
正常创建一个 Spring Boot 的 Maven 项目即可.
在 Spring MVC 中, 拦截器是通过实现 HandlerInterceptor 接口实现的.
在实现 HandlerInterceptor 接口时, 重写里面的拦截器方法.
该方法用于在请求处理之前进行拦截和处理. 该方法在 DispatcherServlet ( 调度器 ) 将请求对象封装为 HandlerExecutionChain 对象后、但在调用处理器( Controller ) 之前被调用
在 preHandle 方法中, 我们可以对请求进行检查、验证、修改等操作, 例如 :
该方法用于在请求处理完成之后, 试图渲染之前进行拦截和处理. 该方法在 HandlerAdapter 将处理器执行并将其结果返回给 DispatcherServlet 之后调用.
在 postHandle 方法中, 我们可以做一些与请求处理有关的操作, 例如 :
该方法用于请求处理完毕后进行回收和清理工作, 该方法在视图渲染完毕之后被调用, 用于释放资源和进行日志记录等操作
在 afterCompletion 方法中, 我们可以进行一些与请求处理过程相关的清理工作, 例如 :
由于我们是一个登陆拦截器, 可以想象在各个功能使用之前就需要进行权限的鉴定和登陆的检验. 因此应该重写 preHandle 方法, 在请求处理之前进行拦截和处理
@Component
public class LoginInterceptor implements HandlerInterceptor {
/**
* 拦截器为调用方法执行之前执行的方法
*
* @param request 请求
* @param response 响应
* @param handler 表示要执行的处理器, 即处理请求的 Controller 对象或者是拦截器的对象
* @return true 拦截器验证成功, 继续执行后续操作
* false 拦截器验证失败, 验证未通过后续流程不执行
* @throws Exception 异常
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 用户登录判断
HttpSession session = request.getSession(false);
if (session != null && session.getAttribute("session_userinfo") != null) {
// 该用户已经登陆
return true;
}
// 请求无权限, 返回状态码提示
response.setStatus(401);
return false;
}
}
有了拦截器, 但还没有实现拦截规则, 并且这个拦截的规则是要随着 Web 项目的启动而启动的, 否则在进行页面访问时, 就无法提前拦截了. 因此还需要将自定义的拦截器添加到我们的系统配置当中.
而在 Spring MVC 中提供了一个 WebMvcConfigurer 接口, 因为我们配置的是拦截器, 因此重写里面的 addInterceptors 方法即可.
在重写的方法中, 将我们实现的登陆拦截器添加进去, 然后配置拦截的规则即可
@Configuration
public class MyInterceptorConfig implements WebMvcConfigurer {
// 将自定义的拦截器注入
@Autowired
private LoginInterceptor interceptor;
/**
* @param registry 注册对象
*/
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 注册对象将拦截器添加进框架中
registry.addInterceptor(interceptor)
// 配置拦截规则
.addPathPatterns("/**") // 拦截所有的 url
.excludePathPatterns("/user/reg") // 排除用户注册 url
.excludePathPatterns("/user/login") // 排除用户登陆 url
.excludePathPatterns("/image/**"); // 排除 image 文件夹底下的所有文件
}
}
一般对于拦截规则的设置, 我们都是先拦截所有, 然后放行部分. 并且里面的路径写法和我们之前学习的相同.
当写好拦截器后, 我们创建一个用户操作类
@RestController
@RequestMapping("/user")
public class UserController {
@RequestMapping("/reg")
public String register() {
return "注册成功";
}
@RequestMapping("/login")
public String login() {
return "登陆成功";
}
@RequestMapping("article")
public String writer() {
return "写文章鉴权";
}
}
根据我们的拦截规则, 我们排除了 url 为 /user/reg 和 user/login 以及 image 文件底下的所有文件. 因此这三个都是可以访问的, 其他的 ulr 都是不可以访问的, 也就是我们上面写的 /user/article 是无法访问的, 会被拦截并返回 401
而我们的 /user/article 被拦截器拦截在外无法执行
对于我们刚刚的做法中, 当被拦截以后, 说明这个方法是不符合要求的请求, 直接返回了 401 拒绝访问, 但在开发中, 无论是否被拦截, 都应该将信息返回给前端, 而并非返回页面而是要一个数据, 前端根据我们返回的数据进行判断后再执行下一步操作.
因此, 我们可以封装一个统一的返回对象 ResultAjax
即使后端拦截了这个方法, 它没有进行登录, 我们也不能帮前端进行重定向让给用户去登录, 这是要越权行为. 我们只需要返回约定的错误代码 code 告诉前端, 并把错误信息返回给前端即可.
@Data
public class ResultAjax {
private int code;
private String msg;
private String date;
}
当被拦截时, 说明为经过登陆鉴权, 返回约定的对象即可
@Component
public class LoginInterceptor implements HandlerInterceptor {
/**
* 拦截器为调用方法执行之前执行的方法
*
* @param request 请求
* @param response 响应
* @param handler 表示要执行的处理器对象, 即处理请求的 Controller 对象或者是拦截器的对象
* @return true 拦截器验证成功, 继续执行后续操作
* false 拦截器验证失败, 验证未通过后续流程不执行
* @throws Exception 异常
*/
@Autowired
private ResultAjax resultAjax;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 用户登录判断
HttpSession session = request.getSession(false);
if (session != null && session.getAttribute("session_userinfo") != null) {
// 该用户已经登陆
return true;
}
ObjectMapper objectMapper = new ObjectMapper();
// 设置数据返回格式, 防止乱码解析
response.setContentType("application/json; charset = utf8");
// 当前方法未通过登陆鉴权, 返回约定的结果
resultAjax.setCode(-1); // 约定错误都为 -1 状态码, 成功都为 200
resultAjax.setMsg("用户未登录, 请登录后再操作 !");
resultAjax.setDate(""); // 为登陆没有数据返回
// 将返回的数据对象转为 JSON 格式字符串
String respJson = objectMapper.writeValueAsString(resultAjax);
// 返回统一的处理对象给前端
response.getWriter().write(respJson);
return false;
}
}
这是一般我们的处理情况, 最好的还是进行一个统一的格式的错误信息返回. 关于统一的数据处理和错误处理, 在后续文章中会讲解到.