SpringMVC 中的Interceptor 拦截器也是相当重要和相当有用的,它的主要作用是拦截用户的请求并进行相应的处理。比如通过它来进行权限验证,或者是来判断用户是否登陆,或者是像12306 那样子判断当前时间是否是购票时间。
SpringMVC 中的Interceptor 拦截请求是通过HandlerInterceptor 来实现的
1,定义一个用于防止重复提交使用的注解 PreventRecoverySubmit
/**
* 用于防止重复提交使用的注解<br>
* 暂时只用于APP无状态需要认证的请求下,简单说有TOKEN参数才能生效.
* @author pgl
*/
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface PreventRecoverySubmit {
/**
* 设置重复提交的唯一标识,默认为按方法唯一
* @return
*/
@AliasFor("key")
String value() default "";
/**
* 设置重复提交的唯一标识,默认为按方法唯一
* @return
*/
@AliasFor("value")
String key() default "";
/**
* 限制多久才能请求一次, 默认为等上次请求成功后就可以立即请求<br>
* 只能为0以上, 否则为默认效果
* @return
*/
int limit() default 0;
/**
* 配合(limit)限制多久所使用计数的单位,默认为分钟
* @return
*/
TimeUnit limitUnit() default TimeUnit.MINUTES;
/**
* 设置请求超时解除限制时间, 单位为分钟, 默认为1分钟<br>
* 当请求超过这个时间就会自动解开提交限制<br>
* 如果设置了限制多久请求一次(limit)配置 则该配置会失效
* @return
*/
int timeout() default 1;
}
2,springMVC.XML中配置拦截地址
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/**"/>
<bean class="com.XXX.security.core.PreventRecoverySubmitInterceptor">
<property name="objectMapper" ref="apiObjectMapper"></property>
</bean>
</mvc:interceptor>
</mvc:interceptors>
3,拦截地址写逻辑处理程序
public class PreventRecoverySubmitInterceptor implements HandlerInterceptor, InitializingBean {
private static final String KEY_PREFIX = "PRS";
@Autowired
private CacheService cacheService;
private ObjectMapper objectMapper;
/**
* 重写InitializingBean接口中的afterPropertiesSet方法
* 实例化bean(初始化清空缓存)
*/
public void afterPropertiesSet() throws Exception {
cacheService.clearCache(KEY_PREFIX);
}
/**
* 重写HandlerInterceptor接口中的preHandle方法
* 方法执行之前调用(只限于@RequestMapping springMVC的请求)
*/
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String token = getToken(request);
PreventRecoverySubmit recoverySubmit = getPreventRecoverySubmit(handler);//获取防重复提交注解对象
if (recoverySubmit != null && StringUtil.isNotEmpty(token)) {//如果注解对象不为null,并且请求对象不为空
String key = getKey(recoverySubmit, handler, token);//生成key
String v = cacheService.get(key);
if (v != null) {
JSONUtil.writerHttpMsg(objectMapper, NotDataResult.Failed("您的操作太频繁, 请稍后重试!"), response);
return false;
}
int timeout = recoverySubmit.timeout();
TimeUnit timeUnit = TimeUnit.MINUTES;//时间单位
if (recoverySubmit.limit() > 0) {//设置了限制多久才能请求一次
timeout = recoverySubmit.limit();
timeUnit = recoverySubmit.limitUnit();
}
cacheService.set(key, handler.toString(), timeout, timeUnit);//加入缓存
}
return true;
}
/**
* 重写HandlerInterceptor接口中的postHandle方法
* 方法执行完之后调用,如果方法异常则不会调用
*/
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
}
private String getToken(HttpServletRequest request) {
String token = request.getHeader("token");
if (token == null)
token = request.getParameter("token");
return token;
}
private PreventRecoverySubmit getPreventRecoverySubmit(Object handler) {
if (handler instanceof HandlerMethod) {
HandlerMethod handlerMethod = (HandlerMethod) handler;
return handlerMethod.getMethodAnnotation(PreventRecoverySubmit.class);
}
return null;
}
/**
* 生成key
* @param recoverySubmit
* @param handler
* @param token
* @return
*/
private String getKey(PreventRecoverySubmit recoverySubmit, Object handler, String token) {
String key = recoverySubmit.value();
if (StringUtil.isEmpty(key)) {
key = MD5Util.getMD5Id(handler.toString());
}
return KEY_PREFIX + ":" + token + ":" + key;
}
/**
* 重写HandlerInterceptor接口中的afterCompletion方法
* 方法执行完之后调用,不管异常都会调用
*/
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
//判断是否设置的请求时间 否则清空缓存
String token = getToken(request);
PreventRecoverySubmit recoverySubmit = getPreventRecoverySubmit(handler);
if (recoverySubmit != null && StringUtil.isNotEmpty(token)) {
String key = getKey(recoverySubmit, handler, token);
if (recoverySubmit.limit() < 1) {
cacheService.del(key);
}
}
}
public void setObjectMapper(ObjectMapper objectMapper) {
this.objectMapper = objectMapper;
}
}
4,需要防止重复提交的方法上添加注解
@PreventRecoverySubmit
@RequestMapping(value = "/applyOrder", method = RequestMethod.POST)
public @ResponseBody Boolean abc() {}