springMvc 攔截器 防止重複提交
1.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之前新增
或是直接在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>
簡單的頁面效果