springMvc 拦截器 防止重复提交
1.DispatcherServletSpringMVC具有统一的入口DispatcherServlet,所有的请求都通过DispatcherServlet。DispatcherServlet是前置控制器,配置在web.xml文件中的。拦截匹配的请求,Servlet拦截匹配规则要自已定义,把拦截下来的请求,依据XX规则分发到目Controller层来处理。 所以我们现在web.xml...
1.DispatcherServlet
SpringMVC具有统一的入口DispatcherServlet,所有的请求都通过DispatcherServlet。DispatcherServlet是前置控制器,配置在web.xml文件中的。拦截匹配的请求,Servlet拦截匹配规则要自已定义,把拦截下来的请求,依据XX规则分发到目Controller层来处理。 所以我们现在web.xml中加入以下配置:
<!-- spring mvc的核心类 -->
<servlet>
<servlet-name>mvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- 可以指定扫描的spring.xml文件 -->
<!--<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:cn/et/day5/resource/spring.xml</param-value>
</init-param>
--><!-- 启动实例化 -->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>mvc</servlet-name>
<url-pattern>/</url-pattern> 所有的请求都会被DispatcherServlet处理
</servlet-mapping>
2、静态资源不用拦截
如果只配置拦截类似于*.do格式或其他格式的url(<url-pattern>*.do</url-pattern>),则对静态资源的访问是没有问题的,但是如果配置拦截了所有的请求(如我们上面配置的“/”),就会造成js文件、css文件、图片文件等静态资源无法访问。一般实现拦截器主要是为了权限管理,主要是拦截一些url请求,所以不对静态资源进行拦截。要过滤掉静态资源一般在
DispatcherServlet之前添加 <mvc:default-servlet-handler />把所有的静态资源交给servlet处理 这样导致Springmvc无法运行 需要在springmvc中添加 <mvc:annotation-driven/>
或是直接在springmvc.xml中添加静态资源的映射
<mvc:resources location="/WEB-INF/imgs/" mapping="/img/**"></mvc:resources>
3、拦截器
SpringMVC的拦截器HandlerInterceptorAdapter对应提供了三个preHandle,postHandle,afterCompletion方法。
preHandle在业务处理器处理请求之前被调用,
postHandle在业务处理器处理请求执行完成后,生成视图之前执行,
afterCompletion在DispatcherServlet完全处理完请求后被调用,可用于清理资源等 。
所以要想实现自己的权限管理逻辑,需要继承HandlerInterceptorAdapter并重写其三个方法。
使用拦截器实现防止重复提交
表单的重复提交:
一、重复提交的情况:
①.在表单提交到一个Servlet中,而Servlet又通过请求转发的方式响应一个JSP页面,此时地址栏还保留着Servlet的那个路径,在相应页面点击"刷新"
②.由于网络原因在相应页面没有到达是重复点击提交表单
③.点击"返回",然后再次点击"提交"
④.重定向还会重现上面②③点描述的情况,但是重定向后地址栏路径会发生改变,故不会出现①的情况
二、不是重复提交的情况
点击"返回","刷新"原表单页面,再"提交",不属于重复提交情况
解决表单的重复提交
使用session设置令牌
产生页面时,服务器为每次产生的Form分配唯一的随机标识号,并且在form的一个隐藏字段中设置这个标识号,
同时在当前用户的Session中保存这个标识号。当提交表单时,服务器比较hidden和session中的标识号是否相同,
相同则继续,处理完后清空Session,否则服务器忽略请求。
以扣款为例
自定义实现一个拦截器
实现HandlerInterceptor接口 重写public boolean preHandle(HttpServletRequestrequest,HttpServletResponseresponse, Object handler)方法
package cn.et.demo05.util;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class Test2 implements HandlerInterceptor {
public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception {
String money = httpServletRequest.getParameter("money");
if ("".equals(money) || money == null) {
httpServletResponse.setContentType("text/html;charset=UTF-8");
httpServletResponse.getWriter().println("转账金额不能为空!!");
return false;
}
// 取出表单中的UUID
String formUUID = httpServletRequest.getParameter("token");
// session中的UUID
Object sessionUUID = httpServletRequest.getSession().getAttribute("token");
System.out.println("表单uuid: " + formUUID);
System.out.println("session: " + sessionUUID);
// 如果请求参数中带有UUID,则表示需要进行防止重复提交处理
if (formUUID != null) {
if (sessionUUID != null) { // session中的uuid不为空,表示表单第一次提交
if (formUUID.equals(sessionUUID.toString())) { // 相同则通过
httpServletRequest.getSession().removeAttribute("token");
return true;
}
}
// 其余情况:1.session中没有UUID、2.session中的UUID跟参数中的UUID不一致。返回false
return false;
}
return true;
}
public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {
}
public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {
}
}
2.配置mvc-servlet.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans
xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.2.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.2.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.2.xsd
">
<!-- 默认的注解映射的支持 -->
<mvc:annotation-driven/>
<!-- 自动扫描-->
<context:component-scan base-package="cn"></context:component-scan>
<mvc:interceptors>
<mvc:interceptor>
<!-- 拦截指定的映射路径 -->
<mvc:mapping path="/demo05/test2" />
<bean id="test2" class="cn.et.demo05.util.Test2"></bean>
</mvc:interceptor>
</mvc:interceptors>
<!-- 扫描jdbc.properties -->
<context:property-placeholder location="classpath:jdbc.properties"/>
<!-- 数据源只是为了获取连接 ${username}是个关键字 默认获取操作系统的用户名 Administrator-->
<bean id="dataSouce" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="url" value="${url}" ></property>
<property name="username" value="${username1}" ></property>
<property name="password" value="${password1}" ></property>
<property name="driverClassName" value="${driverClass}" ></property>
</bean>
<!-- jdbcTemplate 是jdbc的一个模板 封装一些简单的增删改查方法 只要传入sql语句就可以了 -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSouce"></property>
</bean>
</beans>
3.编写mapper层
package cn.et.demo05.mapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;
@Repository
public class MoneyMapper {
@Autowired
JdbcTemplate jdbcTemplate;
public void updateMoney(Integer money){
String sql ="update mymoney set money = money-"+money+" where id=1";
jdbcTemplate.execute(sql);
}
public Integer selectMoney(){
String sql ="select money from mymoney where id=1";
Integer num = jdbcTemplate.queryForObject(sql, Integer.class);
return num;
}
}
4.编写controller层
package cn.et.demo05.controller;
import cn.et.demo05.mapper.MoneyMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import java.io.IOException;
import java.io.OutputStream;
@Controller
public class InterController {
@Autowired
MoneyMapper moneyMapper;
/**
* 演示扣款防止重复提交的例子
* @param money
* @param os
* @return
* @throws Exception
*/
@RequestMapping( value="/demo05/test2")
public String reg(Integer money ,OutputStream os) throws Exception{
moneyMapper.updateMoney(money);
os.write(("money is: "+ moneyMapper.selectMoney()).getBytes());
return null;
}
}
5.在WEB-INF中创建一个tags文件夹然后建立一个后缀名为.tag的文件 放自定义标签 包含生成一个随机数 把随机数放入session中
<%@ tag language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%
// 创建UUID作为随机值
String uuid = UUID.randomUUID().toString();
// 存储到session中
session.setAttribute("token" , uuid);
%>
<input type="hidden" name="token" value="<%=uuid%>"/>
6.编写前段页面
<%@ taglib tagdir="/WEB-INF/tags" prefix="t" %>
<form action="${pageContext.request.contextPath}/tm">
扣款:<input type="text" name="money">
<input type="submit" value="转账">
<t:token></t:token>
</form>
简单的页面效果
更多推荐
所有评论(0)