实现思路:当进入到页面生成token。进行表单提交后。校验token。第一次提交成功消除token信息。多次提交发现token不存在。校验不通过
用的如下类:
GenerateToken注解标识在方法上。自动生成token。返回到页面。由下一次表单提交验证
ValidateToken注解标识在方法上。用来验证提交来的信息是否有token信息。以及验证重复提交
AopRejectMultSubmitConfig类基于aop代理。进行切入点切入。GenerateToken注解再方法进入之前进行生成token。ValidateToken注解进行的是环绕同通知。验证token.如果通过。调用方法
代码如下:
GenerateToken注解
==================GenerateToken=======================
@Documented @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface GenerateToken { String size() default "64"; }
===================GenerateToken======================
ValidateToken注解 ===================ValidateToken====================== @Target(ElementType.METHOD) @Documented @Retention(RetentionPolicy.RUNTIME) public @interface ValidateToken { }
===================ValidateToken======================
AopRejectMultSubmitConfig 类
/** * author:wwz */ @Component @Aspect public class AopRejectMultSubmitConfig { private final static Logger logger = LoggerFactory.getLogger(AopRejectMultSubmitConfig.class); @Autowired private RedisTemplate redisTemplate; private static final String PROJ_ST_MENU = "proj:menu:"; private static final int MAX_AGE = 600; private static class LockHolder{ private static Lock lock = new ReentrantLock(true); } ; /** * 生成token */ @Before("@annotation(com.x.modules.gcjsy.basic.annoation.GenerateToken)") public void before(){ HttpServletResponse response = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse(); HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); if(!isHaveToken(request)){ String uuid = UUID.randomUUID().toString(); initCookie(response,uuid,MAX_AGE); UserSession userSession = PermissionHelper.currentUserSession(); redisTemplate.opsForValue().set(PROJ_ST_MENU+uuid+":"+request.getRequestURI()+":"+userSession.getUserId(),request.getRequestURI(),MAX_AGE, TimeUnit.SECONDS); } } /** * 判断是否有token * @param request * @return */ private boolean isHaveToken(HttpServletRequest request){ String value = getValue(request); if(value == null){ return false; } UserSession userSession = PermissionHelper.currentUserSession(); Object url = redisTemplate.opsForValue().get(PROJ_ST_MENU+value+":"+request.getRequestURI()+":"+userSession.getUserId()); if(url == null||"".equals(url)||!request.getRequestURI().equals(url)){ return false; }; return true; } private void initCookie(HttpServletResponse response,String uuid,int maxAge){ Cookie cookie = new Cookie("tokenUUID",uuid); cookie.setPath("/"); cookie.setMaxAge(maxAge); cookie.setHttpOnly(true); response.addCookie(cookie); } private String getValue(HttpServletRequest request){ Cookie[] cookies = request.getCookies(); for(Cookie cookie:cookies) { if ("tokenUUID".equals(cookie.getName())) { return cookie.getValue(); } } return null; } /** * 校验token */ @Around("@annotation(com.x.modules.gcjsy.basic.annoation.ValidateToken)") public Object methodBefore(ProceedingJoinPoint proceedingJoinPoint) throws Throwable { HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); HttpServletResponse response = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse(); String uuid = getValue(request); if(uuid == null){ throw new Exception("请刷新重试!"); } UserSession userSession = PermissionHelper.currentUserSession(); String userId = userSession.getUserId(); String referer = request.getHeader("referer"); if(referer!=null){ referer = referer.substring(referer.indexOf("/c")); } String key = PROJ_ST_MENU+uuid+":"+referer+":"+userId; Object value = redisTemplate.opsForValue().get(key); if(value!=null&&value.equals(referer)){ try{ //此除加锁。如果是分布式系统。更改成分布式锁 if(LockHolder.lock.tryLock(10,TimeUnit.SECONDS)){ redisTemplate.expire(key,0,TimeUnit.SECONDS); initCookie(response,"",0); return proceedingJoinPoint.proceed(proceedingJoinPoint.getArgs()); } }catch (Exception e){ e.printStackTrace(); /** * 异常回滚 */ initCookie(response,uuid,MAX_AGE); redisTemplate.opsForValue().set(key,referer,MAX_AGE, TimeUnit.SECONDS); throw new Exception("操作异常"); }finally { LockHolder.lock.unlock(); } } logger.error("请不要重复操作"); return null; } } 如何使用?
跳转到表单页面增加@GenerateToken注解如下:此页面会自动生成tokenUUID。
进行表单提交方法上增加@ValidateToken注解如下:会进行验证token信息
验证通过。调用方法。验证失败,进行错误提示: