Springboot+redis+カスタム注釈+ブロッキングによる重複コミット防止


プロセスの処理
ユーザがフォームにアクセスするページの追加->spring反復防止tokenブロッキング要求urlを行い、urlに対応するcontrollerメソッドが反復防止tokenを生成する識別子が注記されているかどうかを判断->反復防止tokenを生成してredisに保存するRedisUtilである.getRu().setex(“formToken_” + uuid, “1”, 60 * 60);同時に、今回生存する重複防止tokenをセッションに入れる->フォームページにジャンプするときに再tokenからフォームを取り出す->ユーザ記入完了情報提出フォーム->spring重複防止tokenブロック要求urlを取り出し、提出url対応controllerメソッドが重複防止提出tokenを処理する識別子を注記しているかどうかを判断する->redisでformTokenを原子減1操作RedisUtilとする.getRu().decr(“formToken_” + clinetToken);redisでマイナス1操作をして値が0でない場合は、繰り返しコミットでfalseを返します.
カスタム注記
カスタム注記を使用する理由
ユーザーがログインしてからアクセスできるなど、特定のユーザーにしかアクセスできない方法があります.Springのブロッキングはブロッキングのルーティングを構成できますが、restfulスタイルのルーティングでは重複することが多く、http methodに基づいて機能を指定するので、そのままブロッキングルーティングルールを構成するのも不便です.したがって、ログインが必要なメソッドに注釈をカスタマイズし、アクセスするメソッドにカスタム注釈があるかどうかをブロッカーで判断し、ある場合は現在のユーザーがログインしているかどうかを判断し(ログイン後に取得したtokenを持っているかどうかを判断)、ブロックするかどうかを決定することができます.
@Target(ElementType.METHOD)

    @Retention(RetentionPolicy.RUNTIME)

    public @interface AnnotationToken {

        /**

         *             URL   controller         ,    token(   uuid)

         * @return

         */

        boolean repeat() default false;

        /**

         *                URL controller         ,          remove token

         * @return

         */

        boolean checkRepeat() default false;

    }

ブロッキング関連の設定
  • ブロッキング
     public class TokenInterceptor extends HandlerInterceptorAdapter {
     
                 private static final Logger logger = Logger.getLogger(TokenInterceptor.class);
     
                 @Autowired
     
                 private RedisUtils redisUtils;
     
                 @Override
     
                 public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler){
     
                     //             
     
                     if(!(handler instanceof HandlerMethod)){
     
                         return true;
     
                     }
     
                     HandlerMethod handlerMethod = (HandlerMethod)handler;
     
                     Method method = handlerMethod.getMethod();
     
                     //    
     
                     AnnotationToken annotation = method.getAnnotation(AnnotationToken.class);
     
                     //      ,    
     
                     if(annotation == null){
     
                         return true;
     
                     }
     
                     //    repeat
     
                     boolean needrepeat = annotation.repeat();
     
                     //       , redis      
     
                     if(needrepeat){
     
                         String uuid = UUID.randomUUID().toString();
     
                         String key = "token" + uuid;
     
                         System.out.println("uuid:"+uuid);
     
                         redisUtils.set("token_"+uuid,"1",60*60);
     
                         request.getSession().setAttribute("token",uuid);
     
                         logger.warn(request.getServletPath() + "---->formToken:" + uuid);
     
                     }
     
                     //    checkRepeat,        token,       ,            
     
                     boolean needCheckRepeat = annotation.checkRepeat();
     
                     if(needCheckRepeat){
     
                         if(isRepeatSubmit(request)){
     
                             logger.warn("please don't repeat submit,url:" + request.getServletPath());
     
                             return false;
     
                         }
     
                     }
     
                     return true;
     
                 }
     
             
     
                 private boolean isRepeatSubmit(HttpServletRequest request){
     
                     String token = (String)request.getSession().getAttribute("token");
     
                     //  redis         token
     
                     boolean r = redisUtils.hasKey("token"+token);
     
                     if(r){
     
                         //  token  ,    0,       
     
                         Long count = redisUtils.decr("token"+token,1);
     
                         redisUtils.del("token"+token);
     
                         if(count == 0){
     
                             //    token
     
                             return false;
     
                         }
     
                     }
     
                     return true;
     
                 }
     
             }
    
  • を追加
  • springにブロッカー
     @Configuration
     
             public class WebAppConfig extends WebMvcConfigurerAdapter {
     
             
     
                 @Override
     
                 public void addInterceptors(InterceptorRegistry registry){
     
                     //        
     
                     registry.addInterceptor(tokenInterceptor()).addPathPatterns("/**");    //       ,        @LoginRequired            
     
                     super.addInterceptors(registry);
     
                 }
     
             
     
                 @Bean
     
                 public TokenInterceptor tokenInterceptor(){
     
                     return new TokenInterceptor();
     
                 }
     
             }
    
  • を登録する.
    Redis関連の構成とツールクラスの追加
  • redis関連構成Resource/conf/redis.properties
     	redis.host=127.0.0.1
     	
     	redis.port=6379
     	
     	redis.timeout=10000
     	
     	redis.maxIdle=300
     	
     	redis.maxTotal=1000
    
  • を追加する.
  • redisツールクラス
         public class RedisUtils {
     	 @Autowired
    
             private RedisTemplate redisTemplate;
    
         //    public void setRedisTemplate(RedisTemplate redisTemplate) {
    
         //        this.redisTemplate = redisTemplate;
    
         //    }
    
             //=============================common============================
    
             /**
    
              *         
    
              * @param key  
    
              * @param time   ( )
    
              * @return
    
              */
    
             public boolean expire(String key,long time){
    
                 try {
    
                     if(time>0){
    
                         redisTemplate.expire(key, time, TimeUnit.SECONDS);
    
                     }
    
                     return true;
    
                 } catch (Exception e) {
    
                     e.printStackTrace();
    
                     return false;
    
                 }
    
             }
    
         
    
             /**
    
              *   key       
    
              * @param key      null
    
              * @return   ( )   0       
    
              */
    
             public long getExpire(String key){
    
                 return redisTemplate.getExpire(key,TimeUnit.SECONDS);
    
             }
    
         
    
             /**
    
              *   key    
    
              * @param key  
    
              * @return true    false   
    
              */
    
             public boolean hasKey(String key){
    
                 try {
    
                     return redisTemplate.hasKey(key);
    
                 } catch (Exception e) {
    
                     e.printStackTrace();
    
                     return false;
    
                 }
    
             }
        }
    
  • を追加
  • redisを追加する構成クラス
     @PropertySource("classpath:conf/redis.properties")
     @Configration
     public class RedisConfig {
    
         @Value("${redis.host}")
    
         private String host;
    
         @Value("${redis.port}")
    
         private Integer port;
    
         @Value("${redis.maxTotal}")
    
         private Integer maxTotal;
    
         @Value("${redis.maxIdle}")
    
         private Integer maxIdle;
    
         @Value("${redis.timeout}")
    
         private Integer timeout;
    
     
    
         @Bean
         public JedisPoolConfig jedisPoolConfig(){
    
             JedisPoolConfig config = new JedisPoolConfig();
    
             config.setMaxTotal(maxTotal);
    
             config.setMaxIdle(maxIdle);
    
             return config;
    
         }
    
     
    
         @Bean
         public JedisConnectionFactory jedisConnectionFactory(JedisPoolConfig config){
    
             JedisConnectionFactory factory = new JedisConnectionFactory(config);
    
             factory.setHostName(host);
    
             factory.setPort(port);
    
             factory.setTimeout(timeout);
    
             return factory;
    
         }
    
     
    
         @Bean
         public RedisTemplate redisTemplate(JedisConnectionFactory factory){
    
             RedisTemplate redisTemplate = new RedisTemplate();
    
             redisTemplate.setKeySerializer(new StringRedisSerializer());
    
             redisTemplate.setHashKeySerializer(new StringRedisSerializer());
    
             redisTemplate.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());
    
             redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());
    
             //     
    
             redisTemplate.setEnableTransactionSupport(true);
    
             redisTemplate.setConnectionFactory(factory);
    
             return redisTemplate;
    
         }
     }