拦截器概念
这里引用百度百科里面的说法,java里的拦截器是动态拦截Action调用的对象。它提供了一种机制可以使开发者可以定义在一个action执行的前后执行的代码,也可以在一个action执行前阻止其执行,同时也提供了一种可以提取action中可重用部分的方式。在AOP(Aspect-Oriented Programming)中拦截器用于在某个方法或字段被访问之前,进行拦截然后在之前或之后加入某些操作
用途
- 日志记录:记录请求的一些信息和请求参数
- 权限校验:如登陆校验,管理员权限校验
- 性能监控:监控请求处理的时间
- 通用行为:如读取cookie信息等
配置Spring MVC拦截器
配置 dispatch-servlet.xml
<mvc:interceptors>
<!-- 如果在interceptors中配置bean,那么所有定义在这里的bean都会被拦截-->
<!--<bean></bean>-->
<mvc:interceptor>
<mvc:mapping path="/manage/**"/>
<!--过滤登陆死循环情况-->
<!--<mvc:exclude-mapping path="/manage/login.do"/>-->
<bean class="com.test.controller.common.interceptor.AuthorityInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
分析配置中的节点
interceptors
节点:这个节点是SpringMVC的拦截器集配置节点,在这个节点里面我们可以声明多个interceptorinterceptor
节点:这个节点是配置拦截路径以及你拦截器实现类的节点
mapping
节点:符合mapping路径匹配的请求都会经过拦截器exclude-mapping
节点:符合exclude-mapping路径匹配的请求都不会经过拦截器bean
节点:配置我们自己的实现类,实现类要实现HandlerInterceptor
接口
path情况分析
- 如果要拦截同一个controller下的所有的请求,如
/manage/***.do
,path应该为/manage/*
- 如果要拦截请求路径为manage包下不同controller的所有请求,如
/manage/product/b.do
,/manage/order/list.do
,path应该为/manage/**
- 如果想要某个请求不走拦截器,那么可以配置
exclude-mapping
,path
应该为具体的url,准确到方法,如/manage/login.do
spring MVC拦截器请求流程
spring请求流程具体如下:
- 浏览器/客户端发送请求给我们的服务器
- 请求来到Spring的
dispatchServlet
进行请求分发 dispatchServlet
检查我们在dispatch-servlet.xml
定义好的规则,按照规则判断请求是否走拦截器- 如果判断请求不走拦截器,请求直接分发到对应的
controller
(这里默认有对应的controller
) - 如果判断请求是走拦截器的,就走我们自己实现的拦截器,验证请求,通过就放行,走到
controller
,反之则提前返回给客户端
spring请求流程图
实现拦截器示例(java)
这是自己实现的一个统一登陆校验拦截器,具体代码如下,写代码之前一定要根据自己的实际情况,把dispatch-servlet.xml
中的拦截器路径配置好,不然拦截器无法正常工作
@Slf4j
public class AuthorityInterceptor implements HandlerInterceptor{
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
log.info(("preCompletion"));
//请求中controller的方法名
HandlerMethod handlerMethod = (HandlerMethod)handler;
//解析handlermethod
//获取方法名
String methodName = handlerMethod.getMethod().getName();
//获取类名,simplename是获取名字不带包名,name是带包名的
String className = handlerMethod.getBean().getClass().getSimpleName();
//解析参数
StringBuffer stringBuffer = new StringBuffer();
Map paramMap = request.getParameterMap();
Iterator it = paramMap.entrySet().iterator();
while ((it.hasNext())){
Map.Entry entry = (Map.Entry) it.next();
String mapKey = (String) entry.getKey();
String mapValue = "";
//request的这个参数map的value返回的是一个String[]
Object obj = entry.getValue();
if (obj instanceof String[]){
String[] strs = (String[])obj;
mapValue = Arrays.toString(strs);
}
stringBuffer.append(mapKey).append("=").append(mapValue);
}
//判断登陆,如果是登陆就放行
if(StringUtils.equals(className, UserManageController.class.getSimpleName())&&StringUtils.equals(methodName,"login")){
//登陆的时候不能把参数的日志也打上,如果日志泄露,账号密码就会泄露
log.info("权限拦截器拦截到的请求 className{} methodName{}",className,methodName);
return true;
}
//判断登陆
User user = session.getAttribute(Const.CURRENT_USER);
if(user == null )){
//未登录,返回false,不会调用controller里面的方法
//response必须重置重置,否则会宝getWritter() has already been called for this response
//这里我们手动接管了SpringMVC原生的返回,而是托管到拦截器中返回
response.reset();
//这是返回编码,否者会乱码
response.setCharacterEncoding("UTF-8");
//设置返回值类型
response.setContentType("application/json;chartset=UTF-8");
PrintWriter out = response.getWriter();
out.print("你想返回的错误信息")
}
out.flush();
out.close();
return false;
}
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
log.info(("postCompletion"));
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
log.info(("afterCompletion"));
}
}