springMvc 拦截器 防止重复提交

1.DispatcherServlet


    SpringMVC具有统一的入口DispatcherServlet,所有的请求都通过DispatcherServlet。

    DispatcherServlet是前置控制器,配置在web.xml文件中的。拦截匹配的请求,Servlet拦截匹配规则要自已定义,把拦截下来的请求,依据XX规则分发到目标Controller层来处理。  所以我们现在web.xml中加入以下配置:

<!-- spring mvc的核心类 -->  
<servlet>  
<span style="white-space:pre">  </span><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></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.day5;  
  
import javax.servlet.http.HttpServletRequest;  
import javax.servlet.http.HttpServletResponse;  
  
import org.springframework.web.servlet.HandlerInterceptor;  
import org.springframework.web.servlet.ModelAndView;  
  
  
  
  
public class KInteractor implements HandlerInterceptor{  
  
    public void afterCompletion(HttpServletRequest request,  
            HttpServletResponse response, Object handler, Exception ex)  
            throws Exception {  
        // TODO Auto-generated method stub  
          
    }  
      
      
    public void postHandle(HttpServletRequest request,  
            HttpServletResponse response, Object handler,  
            ModelAndView modelAndView) throws Exception {  
        // TODO Auto-generated method stub  
          
    }  
      
    //会在action被调用之前执行   返回true通过  返回false不通过  
    public boolean preHandle(HttpServletRequest request,  
            HttpServletResponse response, Object handler) throws Exception {  
          
        //获取请求中的myToken的随机值  
        String m=request.getParameter("myToken");  
          
        //获取第一次访问页面时在session中设的值 (在自定义标签中做的操作)  
        Object m1=request.getSession().getAttribute("myToken");  
        //当m不为空   说明表单提交   需要判断是否是重复提交  
        if(m!=null){  
            //当m1不为空  session中有值   表单不是重复提交  
            if(m1!=null){  
                //为了防止表单参数被篡改   需要判断隐藏表单和session中的值是否相等  
                if(m.equals(m1)){  
                    //把session中的唯一表示符清除  
                    request.getSession().removeAttribute("myToken");  
                    return true;  
                }else{  
                    return false;  
                }  
            }else{  
                return false;  
            }  
        }else{  
            return true;  
        }  
    }  
}  

dao层

package cn.et.springmvc.day5.dao;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;

@Repository
public class MoneyDaoImp {
	@Autowired
	JdbcTemplate jdbc;
	public void trasnateMoney(Integer money){
		String sql ="update mymoney set money=money-"+money+" where id=1";
		jdbc.execute(sql);
	}
	public Integer selectMoney(){
		String sql ="select money from mymoney where id=1";
		Integer num = jdbc.queryForObject(sql, Integer.class);
		return num;
	}
}

controller层

package cn.et.springmvc.day5.controller;

import java.io.OutputStream;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import cn.et.springmvc.day5.dao.MoneyDaoImp;

@Controller
public class InterController {
	@RequestMapping( value="/inter",method=RequestMethod.GET)
	public String reg(OutputStream os) throws Exception{
		os.write("hello".getBytes());
		return null;
	}
	@Autowired
	MoneyDaoImp mdi;
	@RequestMapping( value="/tm",method=RequestMethod.GET)
	public String reg(Integer money ,OutputStream os) throws Exception{
		mdi.trasnateMoney(money);
		os.write(("money is: "+mdi.selectMoney()).getBytes());
		return null;
	}
}

在spring.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:p="http://www.springframework.org/schema/p"
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:mvc="http://www.springframework.org/schema/mvc"
	xsi:schemaLocation="
	http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.1.xsd
	http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd
	http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.1.xsd
	">
	<!-- 扫描文件 -->
	<context:component-scan base-package="cn.et.springmvc.day5"></context:component-scan>

	<!-- pringmvc 配置拦截 / 所有资源都被拦截 图片无法展示 将除控制层以外的资源交给servlet处理 -->
	<mvc:default-servlet-handler />

	<!-- 将springmvc注解的action交给springmvc处理 -->
	<mvc:annotation-driven />
	
	<!-- 防止重复提交拦截器配置 -->
	<mvc:interceptors>
		<mvc:interceptor>
			<!-- 拦截映射路径 -->
			<mvc:mapping path="/tm" />
			<bean id="myInteractor" class="cn.et.springmvc.day5.MyInteractor"></bean>
		</mvc:interceptor>
	</mvc:interceptors>

	<!-- 扫描jdbc.properties -->
	<context:property-placeholder location="classpath:jdbc.properties" /> 
	
	<!-- 创建一个连接数据库的工具 -->
	<bean id="dataSource"
		class="org.springframework.jdbc.datasource.DriverManagerDataSource">
		<property name="url" value="${url}"></property>  <!-- 添加里面的属性 -->
		<property name="username" value="${userid}"></property>
		<property name="password" value="${password}"></property>
		<property name="driverClassName" value="${driverClass}"></property>
	</bean>

	<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
		<property name="dataSource" ref="dataSource"></property>
	</bean>

</beans>

在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%>"/>
<%@ 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>

简单的页面效果



猜你喜欢

转载自blog.csdn.net/Yang_Hui_Liang/article/details/79156478
今日推荐