前言
流程思路
(同Spring MVC一樣)
sessionId
:是Request.getSession().getId() 獲取session裡面的id
url
:是 Request.getRequestURI() 獲取的網址的url
params
:是Request.getParameterMap()轉的json字串再轉的String
表單第一次提交資料:
進入攔截器,從redis 快取中獲取key(sessionId+url)為空,會把sessionId+url為key,url+param為value存入redis快取,返回為true,進入對應 Action
表單第二次提交資料:
進入攔截器,從redis 快取中獲取key(sessionId+url)不為空,然後拿從快取中獲取的值和上一個進行判斷,如果相同則為表單重複提交,返回false;如果不相同,返回為true,進入對應 Action
涉及環境
Jfinal 框架
Tomcat (暫無限制版本) 或 jFinal 的核心伺服器
Redis 快取
jFinal避免表單重複提交配置
表單重複提交攔截器配置
import java.util.HashMap;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import org.apache.log4j.Logger;
import com.jfinal.aop.Interceptor;
import com.jfinal.aop.Invocation;
import com.jfinal.core.Controller;
import net.sf.json.JSONObject;
/**
* @author 馬家立
* @version 建立時間:2018年12月27日下午5:01:37
* @Description: 提交時校驗頁面中url和提交的引數資料並與redis快取中的url校驗,一致透過,時間過期無效
*/
public class RepeatSaveInterceptor implements Interceptor {
private static Logger logger = Logger.getLogger("repeatInterceptor");
//redis 快取的儲存過期時間(單位/s)
static int overTime = 60;
/**
* 表單重複提交攔截器
*/
@Override
public void intercept(Invocation inv) {
// Auto-generated method stub
logger.info("進入重複提交表單攔截器:RepeatSaveInterceptor的intercept");
Controller controller= inv.getController();
//驗證同一個url資料是否相同提交 ,相同返回false
boolean token = repeatDataValidator(controller.getRequest());
if(token){
inv.invoke(); // 繼續執行action中的方法
}
}
/**
* @Title:repeatDataValidator
* @author:馬家立
* @date:2018年12月28日 上午10:37:35
* @Description: 驗證同一個url資料是否相同提交 ,相同返回false
* @param:@param httpServletRequest
* @param:@return true:url或者提交的引數資料不同;false:url相同,提交的引數資料相同
* @return:boolean
*/
public boolean repeatDataValidator(HttpServletRequest httpServletRequest) {
logger.info("進入RepeatSaveInterceptor的repeatDataValidator");
try {
RedisUtil redis = new RedisUtil();
String sessionId = httpServletRequest.getSession().getId();
logger.info("獲得sessionId:" + sessionId);
//獲取引數的map,然後轉為json字串
Map<String, String[]> mapa = httpServletRequest.getParameterMap();
JSONObject jsonObject = JSONObject.fromObject(mapa);
logger.info("jsonObject的輸出結果是:" + jsonObject);
//將json物件轉化為json字串
String params = jsonObject.toString();
String url = httpServletRequest.getRequestURI();
logger.info("url是:" + url);
//建立一個新的map用於儲存新的url和提交的引數資料,用於和session裡面的相比較
Map<String, String> map = new HashMap<String, String>();
map.put(url, params);
String nowUrlParams = map.toString();
Object preUrlParams = redis.get(sessionId + url);
logger.info("表單攔截器配置成功");
// 如果上一個資料為null,表示還沒有訪問頁面
if (preUrlParams == null) {
redis.setex(sessionId + url, overTime, nowUrlParams);
return true;
} else {// 否則,已經訪問過頁面
// 如果上次url+資料和本次url+資料相同,則表示重複新增資料
if (preUrlParams.toString().equals(nowUrlParams)) {
return false;
} else { //如果上次 url+資料 和本次url加資料不同,則不是重複提交
redis.setex(sessionId + url, overTime, nowUrlParams);
return true;
}
}
} catch (Exception e) {
logger.error("進入SameUrlDataInterceptor的repeatDataValidator");
e.printStackTrace();
return false;
}
}
}
表單重複提交日誌配置
#避免表單重複提交
log4j.logger.repeatInterceptor = info,repeatInterceptor
log4j.appender.repeatInterceptor=org.apache.log4j.RollingFileAppender
log4j.appender.repeatInterceptor.File=D:\\ProjectLog\\OASystem\\repeatInterceptor.log
log4j.appender.repeatInterceptor.MaxFileSize=200MB
log4j.appender.repeatInterceptor.MaxBackupIndex=10
log4j.appender.repeatInterceptor.layout=org.apache.log4j.PatternLayout
log4j.appender.repeatInterceptor.layout.ConversionPattern=%-d{yyyy-MM-dd HH\:mm\:ss,SSS}[%p] [%l][%t]%n \u3010%m\u3011%n%n
log4j.additivity.repeatInterceptor=false
Action或 Controller 註解方式使用
在需要進行重複表單驗證的方法上面加上@Before(RepeatSaveInterceptor.java)
註解即可,這個註解即是第一步寫的自定義註解
驗證是否配置成功
檢視log4j的配置路徑下是否生成 repeatInterceptor.log 檔案
關於註解
java中元註解有四個: @Retention @Target @Document @Inherited;
@Retention:註解的保留位置
@Retention(RetentionPolicy.SOURCE) //註解僅存在於原始碼中,在class位元組碼檔案中不包含
@Retention(RetentionPolicy.CLASS) // 預設的保留策略,註解會在class位元組碼檔案中存在,但執行時無法獲得,
@Retention(RetentionPolicy.RUNTIME) // 註解會在class位元組碼檔案中存在,在執行時可以透過反射獲取到
@Target:註解的作用目標
@Target(ElementType.TYPE) //介面、類、列舉、註解
@Target(ElementType.FIELD) //欄位、列舉的常量
@Target(ElementType.METHOD) //方法
@Target(ElementType.PARAMETER) //方法引數
@Target(ElementType.CONSTRUCTOR) //建構函式
@Target(ElementType.LOCAL_VARIABLE)//區域性變數
@Target(ElementType.ANNOTATION_TYPE)//註解
@Target(ElementType.PACKAGE) ///包
@Document:說明該註解將被包含在javadoc中
@Inherited:說明子類可以繼承父類中的該註解