1. 程式人生 > >springMvc 攔截器 防止重複提交

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>

簡單的頁面效果