java+react前后端分离项目处理重复提交问题
2018-06-27 09:45:16来源:博客园 阅读 ()
重复提交的问题在web开发中是很常碰到的一个问题,主要分为前端和后端两种途径解决,前端处理一般采用提交事件后,禁止用户再次点击提交按钮,等待服务端结果再重置提交按钮状态。
本文着重介绍,通过java后端处理重复提交问题。开发环境是:spring boot 2.0+react+ant+dva,下图是主要流程思路:
以下是详细步骤代码:
1:客户端登陆,服务端登陆成功后返回初始的表单令牌
package com.df.web.manager.security; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.util.UUID; /** * @类名称: * @类描述: * @创建人 刘丹 * @创建时间 2018/6/23 * @最后修改人 刘丹. * @最后修改时间 2018/6/23. * @版本:1.0 */ public class FormTokenUtil { public static String refreshFormToken(HttpServletRequest request, HttpServletResponse response) { String newFormToken = UUID.randomUUID().toString(); response.setHeader("formToken", newFormToken); request.getSession(true).setAttribute("formToken", newFormToken); return newFormToken; } }
2:前端获取服务端返回的formToken
sessionStorage.setItem("formToken", resData.result.formToken);
3:在前端统一的request(fetch)的headers中增加表单token项
return request(serviceUrl, { method: "POST", headers: { 'Accept': 'application/json', 'Content-Type': 'application/json', 'formToken': sessionStorage.getItem("formToken") }, body: data, credentials: 'include' });
4:服务端使用aop技术拦截指定注解的Controller请求
package com.df.web.manager.aop; import com.df.web.manager.security.FormTokenUtil; import com.empiresoft.annotation.FormToken; import com.empiresoft.pojo.common.ActionResultGenerator; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; /** * @类名称: 表单重复提交拦截处理 * @类描述: * @创建人 刘丹 * @创建时间 2018/6/23 * @最后修改人 刘丹. * @最后修改时间 2018/6/23. * @版本:1.0 */ @Aspect @Component public class FormTokenAspect { private final Logger logger = LoggerFactory.getLogger(this.getClass()); /** * 对formToken注解的Action执行重复提交验证 * * @param proceedingJoinPoint * @param formToken * @return */ @Around("@annotation(formToken)") public Object execute(ProceedingJoinPoint proceedingJoinPoint, FormToken formToken) { try { ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); HttpServletRequest request = attributes.getRequest(); HttpServletResponse response = attributes.getResponse(); String strFormToken = request.getHeader("formToken"); if (strFormToken == null) { return ActionResultGenerator.errorResult("表单Token不能为空!"); } Object sessionFormToken = request.getSession(true).getAttribute("formToken"); if (sessionFormToken == null || !sessionFormToken.toString().equals(strFormToken)) { return ActionResultGenerator.errorResult("请勿重复提交数据!"); } //放行 Object o = proceedingJoinPoint.proceed(); //重置表单令牌 且写入response 重置前端 表单令牌 FormTokenUtil.refreshFormToken(request, response); return o; } catch (Throwable e) { logger.error(e.getMessage()); return ActionResultGenerator.errorResult("发生异常!"); } } }
5:前端监控Response返回的数据中是否包含表单token项,如果包含则重置前端sessionStorage的表单token。
import fetch from 'dva/fetch'; import { message } from 'antd'; function parseJSON(response) { if (response.headers.get("formToken")) { sessionStorage.setItem("formToken", response.headers.get("formToken")) } return response.json(); } function checkStatus(response) { if (response.status >= 200 && response.status < 300) { return response; } } /** * Requests a URL, returning a promise. * * @param {string} url The URL we want to request * @param {object} [options] The options we want to pass to "fetch" * @return {object} An object containing either "data" or "err" */ export default function request(url, options) { return fetch(url, options) .then(checkStatus) .then(parseJSON) .then(data => ({ data })) .catch((err) => { }); }
注解定义:
package com.empiresoft.annotation; import java.lang.annotation.*; /** * @类名称:FormToken注解类 * @类描述:使用此注解 则表示需要验证FormToken, 用于处理表单重复提交 * @创建人 刘丹 * @创建时间 2018/6/23 * @最后修改人 刘丹. * @最后修改时间 2018/6/23. * @版本:1.0 */ @Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface FormToken { }
标记需要重复提交验证
@FormToken @RequestMapping(value = "/call_service", method = RequestMethod.POST) public ActionResult callServiceByPost(@RequestBody CallService callService) throws Exception { return OauthClientUtil.callUnifiedPlatformService(callService, SecurityUtil.getLoginUser(request), request); }
注:如需允许用户不同的表单使用不同的表单token,只对同性质表单做重复提交验证,可在前后端对token名称"formToken"的命名做扩展处理。
标签:
版权申明:本站文章部分自网络,如有侵权,请联系:west999com@outlook.com
特别注意:本站所有转载文章言论不代表本站观点,本站所提供的摄影照片,插画,设计作品,如需使用,请与原作者联系,版权归原作者所有
上一篇:SpringMVC的理解
- SpringBoot + Vue + ElementUI 实现后台管理系统模板 -- 后 2020-06-10
- 初识Nginx——前后端发布 2020-06-03
- java 在线网络考试系统源码 springboot mybaits vue.js 前后 2020-05-31
- 在滴滴和头条干了 2 年后端开发,太真实… 2020-05-27
- 后端程序员就靠它吃饭了,推荐一份夯实基础的操作系统书单! 2020-05-16
IDC资讯: 主机资讯 注册资讯 托管资讯 vps资讯 网站建设
网站运营: 建站经验 策划盈利 搜索优化 网站推广 免费资源
网络编程: Asp.Net编程 Asp编程 Php编程 Xml编程 Access Mssql Mysql 其它
服务器技术: Web服务器 Ftp服务器 Mail服务器 Dns服务器 安全防护
软件技巧: 其它软件 Word Excel Powerpoint Ghost Vista QQ空间 QQ FlashGet 迅雷
网页制作: FrontPages Dreamweaver Javascript css photoshop fireworks Flash