统一拦截–拦截器Interceptor
1.概述
- 概念:是一种动态拦截方法调用的机制,类似于过滤器。Spring框架中提供的,用来动态拦截控制器方法的执行。
- 作用:拦截请求,在指定的方法调用前后,根据业务需要执行预先设定的代码。
- 与过滤器Filter的使用差不多。
2.快速入门
- 定义拦截器,实现
HandlerInterceptor
接口,并重写其所有方法。
package com.mannor.Interceptor;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@Component
public class LoinCheckInterceptor implements HandlerInterceptor {
@Override //目标资源方法运行前运行,返回true:放行;false:不放行
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("preHandle...运行了");
return true;
}
@Override //目标资源方法运行后运行
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("postHandle...运行了");
}
@Override //视图渲染完毕后运行,最后运行
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("afterCompletion...运行了");
}
}
- 注册配置拦截器
package com.mannor.Config;
import com.mannor.Interceptor.LoinCheckInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration //声明为配置类
public class WebConfig implements WebMvcConfigurer {
@Autowired
private LoinCheckInterceptor loinCheckInterceptor;
@Override //用来注册拦截器
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(loinCheckInterceptor).addPathPatterns("/**");
}
}
3.拦截路径
- 拦截器可以根据需求,配置不同的拦截路径:
拦截路径 | 含义 | 举例 |
---|---|---|
/* | 一级路径 | 能匹配/depts,/emps,/login,不能匹配/depts/1 |
/** | 任意级路径 | 能匹配/depts,/depts/1 ,/depts/1/2 |
/depts/* | /depts下的一级路径 | 能匹配/depts/1,不能匹配/depts/1/2,/depts |
/depts/** | /depts下的任意级路径 | 能匹配/depts,/depts/1,/depts/1/2,不能匹配/emps/1 |
addPathPatterns
:拦截的路径。excludePathPatterns
:不拦截的路径。
@Configuration //声明为配置类
public class WebConfig implements WebMvcConfigurer {
@Autowired
private LoinCheckInterceptor loinCheckInterceptor;
@Override //用来注册拦截器
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(loinCheckInterceptor).addPathPatterns("/**").excludePathPatterns("/login");
}
}
4.拦截器-执行流程
- 过滤器和拦截器的执行先后流程:
5.Filter与Interceptor
- 接口规范不同:过滤器需要实现
Filter
接口,而拦截器需要实现HandlerInterceptor
接口。 - 拦截范围不同:过滤器
Filter
会拦截所有的资源,而Interceptor
只会拦截Spring环境中的资源
6.登录校验的实现–Interceptor
- 步骤(与
Filter
一样)
- 获取请求url。
- 判断请求url中是否包含login,如果包含,说明是登录操作,放行。
- 获取请求头中的令牌( token) 。
- 判断令牌是否存在,如果不存在,返回错误结果(未登录)。
- 解析token,如果解析失败,返回错误结果(未登录)。
- 放行。
package com.mannor.Interceptor;
import com.alibaba.fastjson.JSONObject;
import com.mannor.pojo.Result;
import com.mannor.utils.JwtUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@Slf4j
@Component
public class LoinCheckInterceptor implements HandlerInterceptor {
@Override //目标资源方法(Controller方法)运行前运行,返回true:放行;false:不放行
public boolean preHandle(HttpServletRequest req, HttpServletResponse resp, Object handler) throws Exception {
System.out.println("preHandle...运行了");
//1. 获取请求url。
String requestURI = req.getRequestURI();
log.info("请求的url:{}", requestURI);
//2.判断请求url中是否包含login,如果包含,说明是登录操作,放行。
if (requestURI.contains("login")) {
log.info("登录操作");
return true;
}
//3.获取请求头中的令牌(token)。
String jwt = req.getHeader("token");
//4.判断令牌是否存在,如果不存在,返回错误结果(未登录)。
if (!StringUtils.hasLength(jwt)) {
log.info("请求头token为空,返回未登录的信息");
Result error = Result.error("NOT_LOGIN");//前端需要响应一个json格式的数据
//手动将对象转换为json格式的数据-------》使用阿里巴巴fastJSON的工具包(需要引入依赖)
String notLogin = JSONObject.toJSONString(error);
resp.getWriter().write(notLogin);//使用HttpServletResponse对象返回错误的JSON数据
return false;
}
//5.解析token(校验jwt令牌),如果解析失败,返回错误结果(未登录)。如果错误会报错,所以使用try...catch...
try {
JwtUtils.parseJWT(jwt);
} catch (Exception e) {//程序执行到这里说明程序执行失败--jwt令牌错误
e.printStackTrace();
log.info("解析令牌失败,返回未登录的错误信息");
Result error = Result.error("NOT_LOGIN");//前端需要响应一个json格式的数据
//手动将对象转换为json格式的数据-------》使用阿里巴巴fastJSON的工具包(需要引入依赖)
String notLogin = JSONObject.toJSONString(error);
resp.getWriter().write(notLogin);//使用HttpServletResponse对象返回错误的JSON数据
return false;
}
//6.放行。
log.info("令牌合法,直接放行");
return true;
}
@Override //目标资源方法运行后运行
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("postHandle...运行了");
}
@Override //视图渲染完毕后运行,最后运行
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("afterCompletion...运行了");
}
}
除此之外,还需要在配置类
WebConfig.java
中使用excludePathPatterns()
方法对css
、vue
,js
等静态资源放行,不然Interceptor会产生拦截