Springboot+redis+カスタム注釈+ブロッキングによる重複コミット防止
8298 ワード
プロセスの処理
ユーザがフォームにアクセスするページの追加->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を持っているかどうかを判断)、ブロックするかどうかを決定することができます.
ブロッキング関連の設定ブロッキング を追加 springにブロッカー を登録する.
Redis関連の構成とツールクラスの追加 redis関連構成 を追加する. redisツールクラス を追加 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;
}
}
@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関連の構成とツールクラスの追加
Resource/conf/redis.properties
redis.host=127.0.0.1
redis.port=6379
redis.timeout=10000
redis.maxIdle=300
redis.maxTotal=1000
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;
}
}
}
@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;
}
}