国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁(yè) > 開(kāi)發(fā) > Java > 正文

Spring注解方式防止重復(fù)提交原理詳解

2024-07-14 08:42:58
字體:
來(lái)源:轉(zhuǎn)載
供稿:網(wǎng)友

Srping注解方式防止重復(fù)提交原理分析,供大家參考,具體內(nèi)容如下

方法一: Springmvc使用Token

使用token的邏輯是,給所有的url加一個(gè)攔截器,在攔截器里面用java的UUID生成一個(gè)隨機(jī)的UUID并把這個(gè)UUID放到session里面,然后在瀏覽器做數(shù)據(jù)提交的時(shí)候?qū)⒋薝UID提交到服務(wù)器。服務(wù)器在接收到此UUID后,檢查一下該UUID是否已經(jīng)被提交,如果已經(jīng)被提交,則不讓邏輯繼續(xù)執(zhí)行下去…**

1 首先要定義一個(gè)annotation: 用@Retention 和 @Target 標(biāo)注接口

@Target(ElementType.METHOD)@Retention(RetentionPolicy.RUNTIME)public @interface Token {  boolean save() default false;  boolean remove() default false;}

2 定義攔截器TokenInterceptor:

 

public class TokenInterceptor extends HandlerInterceptorAdapter {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {  if (handler instanceof HandlerMethod) {    HandlerMethod handlerMethod = (HandlerMethod) handler;    Method method = handlerMethod.getMethod();    Token annotation = method.getAnnotation(Token.class);    if (annotation != null) {      boolean needSaveSession = annotation.save();      if (needSaveSession) {        request.getSession(false).setAttribute("token", UUID.randomUUID().toString());      }      boolean needRemoveSession = annotation.remove();      if (needRemoveSession) {        if (isRepeatSubmit(request)) {          return false;        }        request.getSession(false).removeAttribute("token");      }    }    return true;  } else {    return super.preHandle(request, response, handler);  }}private boolean isRepeatSubmit(HttpServletRequest request) {  String serverToken = (String) request.getSession(false).getAttribute("token");  if (serverToken == null) {    return true;  }  String clinetToken = request.getParameter("token");  if (clinetToken == null) {    return true;  }  if (!serverToken.equals(clinetToken)) {    return true;  }  return false;}}

Spring MVC的配置文件里加入:

<mvc:interceptors>  <!-- 使用bean定義一個(gè)Interceptor,直接定義在mvc:interceptors根下面的Interceptor將攔截所有的請(qǐng)求 -->     <mvc:interceptor>       <mvc:mapping path="/**"/>       <!-- 定義在mvc:interceptor下面的表示是對(duì)特定的請(qǐng)求才進(jìn)行攔截的 -->       <bean class="****包名****.TokenInterceptor"/>     </mvc:interceptor> </mvc:interceptors>@RequestMapping("/add.jspf")@Token(save=true)public String add() {  //省略  return TPL_BASE + "index";} @RequestMapping("/save.jspf")@Token(remove=true)public void save() { //省略}

用法:

在Controller類(lèi)的用于定向到添加/修改操作的方法上增加自定義的注解類(lèi) @Token(save=true)

在Controller類(lèi)的用于表單提交保存的的方法上增加@Token(remove=true)

在表單中增加 用于存儲(chǔ)token,每次需要報(bào)token值傳入到后臺(tái)類(lèi),用于從緩存對(duì)比是否是重復(fù)提交操作

方法二:springboot中用注解方式

每次操作,生成的key存放于緩存中,比如用google的Gruava或者Redis做緩存

定義Annotation類(lèi)

@Target(ElementType.METHOD)@Retention(RetentionPolicy.RUNTIME)@Documented@Inheritedpublic @interface LocalLock {  /**   * @author fly   */  String key() default "";  /**   * 過(guò)期時(shí)間 TODO 由于用的 guava 暫時(shí)就忽略這屬性吧 集成 redis 需要用到   *   * @author fly   */  int expire() default 5;}

設(shè)置攔截類(lèi)

@Aspect@Configurationpublic class LockMethodInterceptor {  private static final Cache<String, Object> CACHES = CacheBuilder.newBuilder()      // 最大緩存 100 個(gè)      .maximumSize(1000)      // 設(shè)置寫(xiě)緩存后 5 秒鐘過(guò)期      .expireAfterWrite(5, TimeUnit.SECONDS)      .build();  @Around("execution(public * *(..)) && @annotation(com.demo.testduplicate.Test1.LocalLock)")  public Object interceptor(ProceedingJoinPoint pjp) {    MethodSignature signature = (MethodSignature) pjp.getSignature();    Method method = signature.getMethod();    LocalLock localLock = method.getAnnotation(LocalLock.class);    String key = getKey(localLock.key(), pjp.getArgs());    if (!StringUtils.isEmpty(key)) {      if (CACHES.getIfPresent(key) != null) {        throw new RuntimeException("請(qǐng)勿重復(fù)請(qǐng)求");      }      // 如果是第一次請(qǐng)求,就將 key 當(dāng)前對(duì)象壓入緩存中      CACHES.put(key, key);    }    try {      return pjp.proceed();    } catch (Throwable throwable) {      throw new RuntimeException("服務(wù)器異常");    } finally {      // TODO 為了演示效果,這里就不調(diào)用 CACHES.invalidate(key); 代碼了    }  }  /**   * key 的生成策略,如果想靈活可以寫(xiě)成接口與實(shí)現(xiàn)類(lèi)的方式(TODO 后續(xù)講解)   *   * @param keyExpress 表達(dá)式   * @param args    參數(shù)   * @return 生成的key   */  private String getKey(String keyExpress, Object[] args) {    for (int i = 0; i < args.length; i++) {      keyExpress = keyExpress.replace("arg[" + i + "]", args[i].toString());    }    return keyExpress;  }}

Controller類(lèi)引用

@RestController@RequestMapping("/books")public class BookController { @LocalLock(key = "book:arg[0]") @GetMapping public String save(@RequestParam String token) {  return "success - " + token; }}

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持VeVb武林網(wǎng)。


注:相關(guān)教程知識(shí)閱讀請(qǐng)移步到JAVA教程頻道。
發(fā)表評(píng)論 共有條評(píng)論
用戶(hù)名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 安庆市| 措美县| 陕西省| 夏河县| 米易县| 正镶白旗| 泰宁县| 奉新县| 邵武市| 板桥市| 海门市| 临高县| 宜良县| 屯门区| 荆州市| 乌拉特后旗| 大关县| 钟山县| 萍乡市| 汾西县| 大姚县| 四会市| 芜湖市| 静海县| 康定县| 平乡县| 兴仁县| 富阳市| 连平县| 迁西县| 阿鲁科尔沁旗| 楚雄市| 广元市| 方城县| 霸州市| 永福县| 阆中市| 堆龙德庆县| 张家界市| 汤阴县| 隆子县|