SpringMVC中有两种很普遍的AOP实现:
1.过滤器(Filter)
2.拦截器(Interceptor)
首先说一下两者之间的区别:
过滤器和拦截器非常相似,但是它们有很大的区别
a.最简单明了的区别就是过滤器可以修改request,而拦截器不能
b.过滤器需要在servlet容器中实现,拦截器可以适用于javaEE,javaSE等各种环境
c.拦截器可以调用IOC容器中的各种依赖,而过滤器不能d.过滤器只能在请求的前后使用,而拦截器可以详细到每个方法
一、HandlerInterceptor定义实现类
拦截器我想大家都并不陌生,最常用的登录拦截、或是权限校验、或是防重复提交、或是根据业务像12306去校验购票时间,总之可以去做很多的事情
应用场景
-
日志记录,可以记录请求信息的日志,以便进行信息监控、信息统计等。
-
权限检查:如登陆检测,进入处理器检测是否登陆,如果没有直接返回到登陆页面。
-
性能监控:典型的是慢日志。
定义一个Interceptor 非常简单,方式也有几种,我这里简单列举两种:
- 类要实现Spring 的HandlerInterceptor 接口;
- 类继承实现了HandlerInterceptor 接口的类,例如:已经提供的实现了HandlerInterceptor 接口的抽象类HandlerInterceptorAdapter。
public interface HandlerInterceptor {
/**
* 预处理回调方法,实现处理器的预处理(如检查登陆),第三个参数为响应的处理器,自定义Controller
* 返回值:true表示继续流程(如调用下一个拦截器或处理器);false表示流程中断
(如登录检查失败),不会继续调用其他的拦截器或处理器,此时我们需要通过response来产生响应;
*/
boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception;
/**
* 后处理回调方法,实现处理器的后处理(但在渲染视图之前),此时我们可以
通过modelAndView(模型和视图对象)对模型数据进行处理或对视图进行处理,modelAndView也可能为null。
*/
void postHandle(
HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)
throws Exception;
/**
* 整个请求处理完毕回调方法,即在视图渲染完毕时回调,如性能监控中我们可以在此记录结束时间并输
出消耗时间,还可以进行一些资源清理,类似于try-catch-finally中的finally,但仅调用处理器执行链中
*/
void afterCompletion(
HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
throws Exception;
}
- preHandle:在业务处理器处理请求之前被调用。预处理,可以进行编码、安全控制、权限校验等处理;
- postHandle:在业务处理器处理请求执行完成后,生成视图之前执行。后处理(调用了Service并返回ModelAndView,但未进行页面渲染),有机会修改ModelAndView (这个博主就基本不怎么用了);
- afterCompletion:在DispatcherServlet完全处理完请求后被调用,可用于清理资源等。返回处理(已经渲染了页面);
拦截器实现
鉴权示例:
import com.alibaba.fastjson.serializer.SerializerFeature;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.http.HttpStatus;
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;
import java.io.PrintWriter;
/**
* 鉴权
*/
@Slf4j
@Component
public class MyInterceptor implements HandlerInterceptor {
private SecurityUser securityUser;
public MyInterceptor (SecurityUser securityUser) {
this.securityUser = securityUser;
}
@Override
public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o)
throws Exception {
String token = securityUser.getToken(httpServletRequest);
if (StringUtils.isEmpty(token) || !securityUser.validateToken(token)) {
log.info("token is empty...");
httpServletResponse.setContentType("application/json;charset=UTF-8");
httpServletResponse.setStatus(HttpStatus.UNAUTHORIZED.value());
// 把json数据返回给浏览器:
final SerializerFeature[] features = {SerializerFeature.DisableCircularReferenceDetect,
SerializerFeature.WriteDateUseDateFormat};
PrintWriter out = httpServletResponse.getWriter();
out.print(ReturnMapUtils.setFailedMsgRtnJsonStr("鉴权失败"));
return false;
} else {
securityUser.checkRenewal(token);
return true; // 只有返回true才会继续向下执行,返回false取消当前请求
}
}
@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 {
}
}
二、WebAppConfigurer
新建WebAppConfigurer:实现WebMvcConfigurer接口,拦截请求
import org.slf4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import java.util.Arrays;
/**
*
*/
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Autowired
private SecurityUser securityUser;
private Logger logger = LoggerUtil.getLogger();
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 自定义拦截器
registry.addInterceptor(new MyInterceptor(securityUser)).addPathPatterns("/**").excludePathPatterns(Arrays.asList("/components/**", "/js/**", "/images/**", "/css/**", "/img/**", "/img/*"));
}
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
String storagePathHead = System.getProperty("user.dir");
String imagesPath = ("file:" + storagePathHead + "/storage/image/");
String videosPath = ("file:" + storagePathHead + "/storage/video/");
String xmlPath = ("file:" + storagePathHead + "/storage/xml/");
logger.info("imagesPath=" + imagesPath);
logger.info("videosPath=" + videosPath);
logger.info("xmlPath=" + xmlPath);
registry.addResourceHandler("/images/**", "/videos/**", "/xml/**").addResourceLocations(imagesPath, videosPath, xmlPath);
// Swagger2Config
registry.addResourceHandler("/**")
.addResourceLocations("classpath:/META-INF/resources/")
.addResourceLocations("classpath:/resources/")
.addResourceLocations("classpath:/static/")
.addResourceLocations("classpath:/public/");
WebMvcConfigurer.super.addResourceHandlers(registry);
}
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("*")
.allowedMethods("GET", "POST", "OPTIONS", "PUT", "DELETE")
.allowedHeaders("*")
.allowCredentials(false).maxAge(3600);
WebMvcConfigurer.super.addCorsMappings(registry);
}
}
WebMvcConfigurer比较常用的方法:
a.addInterceptors:拦截器
addInterceptor:需要一个实现HandlerInterceptor接口的拦截器实例
addPathPatterns:用于设置拦截器的过滤路径规则,增加url的拦截路径,addPathPatterns("/**")对所有请求都拦截
excludePathPatterns:用于设置不需要拦截的过滤规则,排除url的拦截路径,如:"/login", "/register"意为登录和注册不做拦截;
拦截器主要用途:进行用户登陆状态的拦截,日志的拦截等
b.addResourceHandlers:静态资源
比如,我们想自定义静态资源映射目录的话,只需重写addResourceHandlers方法即可
注意:如果继承WebMvcConfigurationSupport类实现配置时必须要重写该方法
addResourceHandler:指的是对外暴露的访问路径
addResourceLocations:指的是内部文件放置的目录
c.addCorsMappings:跨域
@Override
public void addCorsMappings(CorsRegistry registry) {
super.addCorsMappings(registry);
registry.addMapping("/cors/**")
.allowedHeaders("*")
.allowedMethods("POST","GET")
.allowedOrigins("*");
}
三、过滤器(Filter)
Filter的字面意思:过滤器就是过滤的作用,在web开发中过滤一些我们指定的url;比如:
a.拦截掉我们不需要的接口请求
b.修改请求(request)和响应(response)内容
c.完成CORS跨域请求等等
import org.springframework.stereotype.Component;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
*
*/
@Component
public class CorsFilter implements Filter {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
throws IOException, ServletException {
HttpServletResponse response = (HttpServletResponse) servletResponse;
HttpServletRequest request = (HttpServletRequest) servletRequest;
response.setHeader("Access-Control-Allow-Origin", "*");
response.setHeader("Access-Control-Allow-Methods", "POST, GET,OPTIONS");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Expose-Headers", "token");
response.setHeader("Access-Control-Allow-Headers",
"x-requested-with,Authorization,origin,Content-Type,accept,contentType,token");
response.setHeader("Access-Control-Allow-Credentials", "true");
String options = "OPTIONS";
if (options.equalsIgnoreCase(request.getMethod())) {
response.setStatus(HttpServletResponse.SC_OK);
} else {
filterChain.doFilter(servletRequest, servletResponse);
}
}
@Override
public void init(FilterConfig filterConfig) {
}
@Override
public void destroy() {
}
}
初始化(init)和摧毁(destroy)方法一般不会用到,具体使用看下源码便知
doFilter()是过滤器的核心
注意:在实现接口方法之后,我们要转换request和response类型至HttpServlet,否则接下去的操作可能会报错。
如果过滤通过,执行filterChain.doFilter(request,response);说明这个url已经经过了我们的Filter
可以看到,只需要一个类我们就实现了一个简单的过滤器;当然可以不用注解的方式,配置启动类
https://blog.csdn.net/weixin_36307377/article/details/114901647
https://blog.csdn.net/weixin_44259720/article/details/104615086