一个一个来,按spring boot的风格,我们不喜欢xml文件,所以使用java类来启用拦截器配置:
import com.wlf.order.prize.aop.RequestInterceptor;
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 InterceptorConfig implements WebMvcConfigurer {
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new RequestInterceptor()).addPathPatterns("/**").
excludePathPatterns("/jpservice/getSign", "/jpservice/getTimeStamp");
}
}
上面我们拦截了所有的web请求,除了这两个接口:"/jpservice/getSign", "/jpservice/getTimeStamp"
接着我们进入拦截器里,做消息头校验、重复请求校验:
import com.wlf.order.prize.javabean.Result;
import com.wlf.order.prize.util.BeanConvert;
import com.wlf.order.prize.util.IPUtil;
import com.wlf.order.prize.util.ThreadLocalUtil;
import com.wlf.order.prize.util.TimeStampList;
import lombok.extern.slf4j.Slf4j;
import org.springframework.lang.Nullable;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
/**
* 拦截器:增加重复请求的过滤、话单记录处理
*
* @author wulinfeng
* @since 2019/12/25
*/
@Slf4j
@Component
public class RequestInterceptor extends HandlerInterceptorAdapter {
private final static SimpleDateFormat SF = new SimpleDateFormat("yyyyMMddHHmmss");
// 话单格式:记录时间|接口名称|接口时延|调用方IP|本地IP|业务参数|结果码|序列号
private final static String CDR_FORMAT = "{}|{}|{}|{}|{}|{}|{}|{}";
// 时间戳缓存
private final static TimeStampList cache = new TimeStampList(10000);
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 请求头校验
String timestamp = request.getHeader("timestamp");
String sign = request.getHeader("sign");
if (timestamp == null || timestamp.trim().equals("") || sign == null || sign.trim().equals("")) {
Result result = new Result(1025, "请求头缺失.");
getResponse(result, response);
return false;
}
// 时间戳校验
if (timestamp.length() != 13) {
Result result = new Result(1026, "时间戳格式错误.");
getResponse(result, response);
return false;
}
Long requestTime = null;
try {
requestTime = Long.parseLong(timestamp);
} catch (NumberFormatException e) {
Result result = new Result(1026, "时间戳格式错误.");
getResponse(result, response);
return false;
}
// 重复请求校验
if (cache.contains(requestTime)) {
Result result = new Result(1027, "重复请求.");
getResponse(result, response);
return false;
}
cache.add(requestTime);
// 获取请求和本地IP,记录话单
String beginTime = String.valueOf(System.currentTimeMillis());
String remoteIp = IPUtil.getRemoteIp(request);
String localIp = IPUtil.getLocalIp();
Map<String, String> strMap = new HashMap<>();
strMap.put("beginTime", beginTime);
strMap.put("remoteIp", remoteIp);
strMap.put("localIp", localIp);
strMap.put("sequence", timestamp);
ThreadLocalUtil.setMap(strMap);
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
@Nullable ModelAndView modelAndView) throws Exception {
// 计算接口时延
Map<String, String> strMap = ThreadLocalUtil.getMap();
long beginTime = Long.parseLong(strMap.get("beginTime"));
long currentTime = System.currentTimeMillis();
// 获取当前时间
String currentDate = SF.format(new Date(currentTime));
// 记录话单
log.error(CDR_FORMAT, currentDate, strMap.get("apiType"), currentTime - beginTime, strMap.get("remoteIp"),
strMap.get("localIp"), strMap.get("sequence"), strMap.get("biz"), strMap.get("resultCode"));
}
/**
* 构造响应消息体
*
* @param result
* @param response
* @throws IOException
*/
private void getResponse(Result result, HttpServletResponse response) throws IOException {
response.setCharacterEncoding("UTF-8");
response.setContentType("application/json; charset=utf-8");
PrintWriter out = null;
out = response.getWriter();
out.write(BeanConvert.getResultJson(result));
out.flush();
out.close();
}
}
如果你的拦截器需要注入bean,而且失败了,可以参见springboot拦截器注入bean失败实例。