java确认重新提交表单_表单防重提交详解

表单重复提交的常见应用场景

1、在网络延迟的情况下让用户又是加你点击多次submit按钮导致

2、表单提交后用户点击刷新按钮导致表单重复提交

3、用户表单提交后,点击浏览器后退按钮退回表单页面后进行再次提交

很多情况下,重复提交的数据,都不是我们想要的,如订单的提交,申请退款的提交等,那么如何做到防重提交呢?

下面介绍有4种方法,重点最后一种:

1.给数据库所需的字段添加上唯一性约束

此方法最有效的防止了数据重复提交,但是前台还是会出现重复提交的情况,后台回报错.

2.前端使用js在点击按钮提交后设置disable,后或者js设置一个属性,提交前为true,提交后为false

客户端禁用js,这种方法将无效

3.使用Post/Redirect/Get

Post/Redirect/Get简称PRG,是一种可以防止表单数据重复提交的一种Web设计模式,像用户刷新提交响应页面等比较典型的重复提交表单数据的问题可以使用PRG模式来避免。例如:当用户提交成功之后,执行客户端重定向,跳转到提交成功页面。

注意:PRG设计模式并不适用所有的重复提交情况,比如:

1)由于服务器响应缓慢,用户刷新提交POST请求造成的重复提交。

2)用户点击后退按钮,返回到数据提交界面,导致的数据重复提交。

3)用户多次点击提交按钮,导致的数据重复提交。

4)用户恶意避开客户端预防多次提交手段,进行重复数据提交。

4.使用session和注解设置令牌

所谓令牌其实就是一种标识,标识当前提交状态,比如以前古代打战时,皇帝发布命令时会给个虎符给手下的人携带到将军那边传达,而将军手上也有皇帝给的另一半虎符,将军一对比,果然能凑成一对,就根据传达的命令去打战,如果来人没有携带虎符,将军是肯定把来人砍头的.

那么这个令牌怎么设置,下图就是写这个令牌token的一个思路:

88c654729e50a2cbb37c0089deb790ad.png

1.先写一个简单的注解类

import java.lang.annotation.Documented;

import java.lang.annotation.ElementType;

import java.lang.annotation.Retention;

import java.lang.annotation.RetentionPolicy;

import java.lang.annotation.Target;

/**

自定义一个Token注解,用于标识需要防重提交的方法

@author ranger

*

*/

@Target(value=ElementType.METHOD)

@Retention(RetentionPolicy.RUNTIME)

@Documented

public @interface TokenForm {

//用于标记需要防重提方法的 ,创建Token的属性

boolean create() default false;

//用于标记需要防重提方法的,删除Token的属性

boolean remove() default false;

}

2.在跳转到增加页面前,创建token

/**

* 跳转到增加页面

* 请求路径:${pageContext.request.contextPath}/admin/toAdminAdd

* @return

*/

@RequestMapping(value="/toAdminAdd")

@TokenForm(create=true)

public String toAdminAdd(HttpServletRequest request) {

return "manager/adminAdd";

}

3.添加数据后删除token

/**

* 增加管理员

* 请求路径:${pageContext.request.contextPath }/admin/addAdmin

* @param admin

* @param request

* @return

*/

@RequestMapping(value="/addAdmin")

@TokenForm(remove=true)

public String addAdmin(@RequestParam Map admin,HttpServletRequest request) {

try {

//将密码Md5编码后在插入

admin.put("admin_pwd",Md5Utils.md5((String)admin.get("admin_pwd")) );

LOGGER.debug("-增加管理员-"+admin);

adminService.addAdmin(admin);

request.setAttribute("admin_add_msg", "增加管理员成功");

} catch (Exception e) {

e.printStackTrace();

request.setAttribute("admin_add_msg", "增加管理员失败");

}

return "manager/adminAdd";

}

4.创建一个拦截器,拦截发送路径前的token信息

import java.util.UUID;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;

import javax.servlet.http.HttpSession;

import org.apache.log4j.LogManager;

import org.apache.log4j.Logger;

import org.springframework.web.method.HandlerMethod;

import org.springframework.web.servlet.HandlerInterceptor;

import org.springframework.web.servlet.ModelAndView;

import cn.gzsxt.annotation.TokenForm;

public class TokenInterceptor implements HandlerInterceptor {

private static final Logger LOGGER = LogManager.getLogger(TokenInterceptor.class);

@Override

public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)

throws Exception {

//第一步:获得调用处理方法的注解

HandlerMethod hm=(HandlerMethod) handler;

TokenForm tokenForm = hm.getMethodAnnotation(TokenForm.class);

//第二步:判断是否有Token注解

if (tokenForm!=null) {

HttpSession session = request.getSession();

if (tokenForm.create()==true) {

session.setAttribute("token", UUID.randomUUID().toString());

LOGGER.debug("打印出来的token:"+session.getAttribute("token"));

}

if (tokenForm.remove()==true) {

//判断表单的Token与服务端的Token是否相同

String formToken = request.getParameter("token");

Object sessionToken = session.getAttribute("token");

//传递过来的Token与服务端的Token相同,允许操作,并且删除session的Token

if (formToken.equals(sessionToken)){

session.removeAttribute("token");

}else{

//跳转到指定的路径

String invoke = request.getParameter("token.invoke");

response.sendRedirect(request.getContextPath()+invoke);

return false;

}

}

}

return true;

}

@Override

public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,

ModelAndView modelAndView) throws Exception {

// TODO Auto-generated method stub

}

@Override

public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)

throws Exception {

// TODO Auto-generated method stub

}

5.前端指定提交的token和重复提交后可跳转的地址token.invoke

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java实现搜索功能的代码详解: 1. 实现搜索框 首先需要在页面上添加一个搜索框,用户输入关键字后,将关键字传递给后台进行搜索。 ```html <form action="search" method="get"> <input type="text" name="keyword"> <input type="submit" value="搜索"> </form> ``` 2. 处理搜索请求 当用户点击搜索按钮后,会将表单数据提交到后台的 `search` 路径,后台需要处理该请求并返回搜索结果。 ```java @RequestMapping(value = "/search", method = RequestMethod.GET) @ResponseBody public List<Article> search(@RequestParam String keyword) { List<Article> result = new ArrayList<>(); // 根据关键字从数据库中查询相关文章 // 将查询结果添加到 result 中 return result; } ``` 3. 实现搜索算法 根据关键字从数据库中查询相关文章,需要使用搜索算法。这里以简单的关键字匹配算法为例,实现代码如下: ```java for (Article article : articles) { if (article.getTitle().contains(keyword) || article.getContent().contains(keyword)) { result.add(article); } } ``` 4. 显示搜索结果 搜索完成后,需要将搜索结果显示给用户。 ```html <ul> <#list articles as article> <li><a href="/article/${article.id}">${article.title}</a></li> </#list> </ul> ``` 完整代码: ```java @RequestMapping(value = "/search", method = RequestMethod.GET) @ResponseBody public List<Article> search(@RequestParam String keyword) { List<Article> result = new ArrayList<>(); List<Article> articles = articleService.getAllArticles(); for (Article article : articles) { if (article.getTitle().contains(keyword) || article.getContent().contains(keyword)) { result.add(article); } } return result; } ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值