基于springboot+jdk8
本次通过拦截器的方式实现表单重复提交的校验, 可详细交流
注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Token {
boolean verify() default false;
}
拦截器
public class FormTokenInterceptor extends HandlerInterceptorAdapter {
private final String tokenKey = "_token";
private CacheClient cacheClient;
public FormTokenInterceptor() {
cacheClient = (CacheClientImpl) SpringBeanUtils.getBean("cacheClientImpl");
}
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
if (handler instanceof HandlerMethod) {
HandlerMethod handlerMethod = (HandlerMethod) handler;
Method method = handlerMethod.getMethod();
Token annotation = method.getAnnotation(Token.class);
if (annotation != null) {
boolean access = true;
if (annotation.verify()) {
access = verifyToken(request);
}
generateToken(request, response);
if (!access) {
throw new ApplicationException(MessageUtils.get("message-form-repetition-submit"));
}
}
}
return true;
}
private boolean verifyToken(HttpServletRequest request) {
String clientToken = getCookieValue(request, tokenKey);
if (clientToken == null) {
return false;
}
String serverToken = cacheClient.get(clientToken);
if (!clientToken.equals(serverToken)) {
return false;
}
cacheClient.expire(serverToken, 0);
return true;
}
private String getCookieValue(HttpServletRequest request, String key) {
Cookie[] cookies = request.getCookies();
if (cookies != null && cookies.length > 0) {
for (Cookie cookie : cookies) {
if (cookie.getName().equals(key)) {
return cookie.getValue();
}
}
}
return null;
}
private void generateToken(HttpServletRequest request, HttpServletResponse response) {
String token = FormToken.generateToken();
Cookie cookie = new Cookie(tokenKey, token);
cookie.setMaxAge(7200);
cookie.setPath("/");
response.addCookie(cookie);
cacheClient.set(token, token);
cacheClient.expire(token, 7200);
}
}
MVC注册拦截器
@Configuration
public class MyMvcConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new FormTokenInterceptor())
.addPathPatterns("/**")
.excludePathPatterns("/error")
.excludePathPatterns(Arrays.asList("/component/**", "/html/**", "/favicon.ico"));
}
}
使用方式
在需要校验的上一个接口请求添加此注解,设置生成cookie,并将token存入缓存之中
@Token
在需要校验的接口加此注解, 拦截器会校验cookie是否一致,如果不一致不允许提交,无论失败或者成功,都会更新缓存中的token
@Token(verify = true)