diff --git a/ThunderSoft_repeatsubmitfilter/README.md b/ThunderSoft_repeatsubmitfilter/README.md new file mode 100644 index 000000000..f81e306dc --- /dev/null +++ b/ThunderSoft_repeatsubmitfilter/README.md @@ -0,0 +1,19 @@ +# ShortUrl +Repeatsubmitfilter是一个防止重复提交的依赖库 + + +## 逻辑详情 +系统加载直接自动使用 + +## 使用步骤说明 + +1. 应用引用依赖库 +2. 配置应用配置参数 (无需配置) +3. 逻辑调用示例截图 +注意:需要根据自己的业务修改一下放行的白名单 + +参考文档 + +## 应用演示链接 + +[使用了本依赖库的制品应用链接] diff --git a/ThunderSoft_repeatsubmitfilter/lib/nasl-metadata-collector-0.7.0.jar b/ThunderSoft_repeatsubmitfilter/lib/nasl-metadata-collector-0.7.0.jar new file mode 100644 index 000000000..f085868b0 Binary files /dev/null and b/ThunderSoft_repeatsubmitfilter/lib/nasl-metadata-collector-0.7.0.jar differ diff --git a/ThunderSoft_repeatsubmitfilter/pom.xml b/ThunderSoft_repeatsubmitfilter/pom.xml new file mode 100644 index 000000000..56982ed4e --- /dev/null +++ b/ThunderSoft_repeatsubmitfilter/pom.xml @@ -0,0 +1,81 @@ + + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 2.2.9.RELEASE + + + com.hq + repeatsubmitfilter + 1.1.6 + 弱网情况下防止重复提交 + + 8 + 8 + UTF-8 + 3.3 + + + + + nasl-metadata-collector + com.netease.lowcode + 0.7.0 + true + system + ${project.basedir}/lib/nasl-metadata-collector-0.7.0.jar + + + + org.springframework.boot + spring-boot-starter-web + provided + + + + + + org.apache.maven.plugins + maven-assembly-plugin + 3.3.0 + + + jar-with-dependencies + + false + + + + make-assembly + package + + single + + + + + + com.netease.lowcode + nasl-metadata-maven-plugin + 1.3.0 + + true + + + + + archive + + + + + + + diff --git "a/ThunderSoft_repeatsubmitfilter/repeatsubmitfilter\344\276\235\350\265\226\345\214\205.docx" "b/ThunderSoft_repeatsubmitfilter/repeatsubmitfilter\344\276\235\350\265\226\345\214\205.docx" new file mode 100644 index 000000000..36a34f211 Binary files /dev/null and "b/ThunderSoft_repeatsubmitfilter/repeatsubmitfilter\344\276\235\350\265\226\345\214\205.docx" differ diff --git a/ThunderSoft_repeatsubmitfilter/src/main/java/com/cache/Cache.java b/ThunderSoft_repeatsubmitfilter/src/main/java/com/cache/Cache.java new file mode 100644 index 000000000..626886d7e --- /dev/null +++ b/ThunderSoft_repeatsubmitfilter/src/main/java/com/cache/Cache.java @@ -0,0 +1,51 @@ +package com.cache; + + +public class Cache { + private String key;//缓存ID + private Object value;//缓存数据 + private long timeOut;//更新时间 + private boolean expired; //是否终止 + public Cache() { + super(); + } + + public Cache(String key, Object value, long timeOut, boolean expired) { + this.key = key; + this.value = value; + this.timeOut = timeOut; + this.expired = expired; + } + + public String getKey() { + return key; + } + + public long getTimeOut() { + return timeOut; + } + + public Object getValue() { + return value; + } + + public void setKey(String string) { + key = string; + } + + public void setTimeOut(long l) { + timeOut = l; + } + + public void setValue(Object object) { + value = object; + } + + public boolean isExpired() { + return expired; + } + + public void setExpired(boolean b) { + expired = b; + } +} \ No newline at end of file diff --git a/ThunderSoft_repeatsubmitfilter/src/main/java/com/cache/CacheManager.java b/ThunderSoft_repeatsubmitfilter/src/main/java/com/cache/CacheManager.java new file mode 100644 index 000000000..6a548e0f8 --- /dev/null +++ b/ThunderSoft_repeatsubmitfilter/src/main/java/com/cache/CacheManager.java @@ -0,0 +1,198 @@ +package com.cache; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; + + +public class CacheManager { + private static HashMap cacheMap = new HashMap(); + + //单实例构造方法 + private CacheManager() { + super(); + } + //获取布尔值的缓存 + public static boolean getSimpleFlag(String key){ + try{ + return (Boolean) cacheMap.get(key); + }catch(NullPointerException e){ + return false; + } + } + public static long getServerStartdt(String key){ + try { + return (Long)cacheMap.get(key); + } catch (Exception ex) { + return 0; + } + } + //设置布尔值的缓存 + public static boolean setSimpleFlag(String key,boolean flag){ + if (flag && getSimpleFlag(key)) {//假如为真不允许被覆盖 + return false; + }else{ + cacheMap.put(key, flag); + return true; + } + } + public static boolean setSimpleFlag(String key,long serverbegrundt){ + if (cacheMap.get(key) == null) { + cacheMap.put(key,serverbegrundt); + return true; + }else{ + return false; + } + } + + + //得到缓存。同步静态方法 + private synchronized static Cache getCache(String key) { + return (Cache) cacheMap.get(key); + } + + //判断是否存在一个缓存 + private synchronized static boolean hasCache(String key) { + return cacheMap.containsKey(key); + } + + //清除所有缓存 + public synchronized static void clearAll() { + cacheMap.clear(); + } + + //清除某一类特定缓存,通过遍历HASHMAP下的所有对象,来判断它的KEY与传入的TYPE是否匹配 + public synchronized static void clearAll(String type) { + Iterator i = cacheMap.entrySet().iterator(); + String key; + ArrayList arr = new ArrayList(); + try { + while (i.hasNext()) { + java.util.Map.Entry entry = (java.util.Map.Entry) i.next(); + key = (String) entry.getKey(); + if (key.startsWith(type)) { //如果匹配则删除掉 + arr.add(key); + } + } + for (int k = 0; k < arr.size(); k++) { + clearOnly(arr.get(k)); + } + } catch (Exception ex) { + ex.printStackTrace(); + } + } + + //清除指定的缓存 + public synchronized static void clearOnly(String key) { + cacheMap.remove(key); + } + + //载入缓存 + public synchronized static void putCache(String key, Cache obj) { + cacheMap.put(key, obj); + } + + //获取缓存信息 + public static Cache getCacheInfo(String key) { + + if (hasCache(key)) { + Cache cache = getCache(key); + if (cacheExpired(cache)) { //调用判断是否终止方法 + cache.setExpired(true); + } + return cache; + }else + return null; + } + + //载入缓存信息 + public static void putCacheInfo(String key, Cache obj, long dt, boolean expired) { + Cache cache = new Cache(); + cache.setKey(key); + cache.setTimeOut(dt + System.currentTimeMillis()); //设置多久后更新缓存 + cache.setValue(obj); + cache.setExpired(expired); //缓存默认载入时,终止状态为FALSE + cacheMap.put(key, cache); + } + //重写载入缓存信息方法 + public static void putCacheInfo(String key, Cache obj, long dt){ + Cache cache = new Cache(); + cache.setKey(key); + cache.setTimeOut(dt+System.currentTimeMillis()); + cache.setValue(obj); + cache.setExpired(false); + cacheMap.put(key,cache); + } + + //判断缓存是否终止 + public static boolean cacheExpired(Cache cache) { + if (null == cache) { //传入的缓存不存在 + return false; + } + long nowDt = System.currentTimeMillis(); //系统当前的毫秒数 + long cacheDt = cache.getTimeOut(); //缓存内的过期毫秒数 + if (cacheDt <= 0||cacheDt>nowDt) { //过期时间小于等于零时,或者过期时间大于当前时间时,则为FALSE + return false; + } else { //大于过期时间 即过期 + return true; + } + } + + //获取缓存中的大小 + public static int getCacheSize() { + return cacheMap.size(); + } + + //获取指定的类型的大小 + public static int getCacheSize(String type) { + int k = 0; + Iterator i = cacheMap.entrySet().iterator(); + String key; + try { + while (i.hasNext()) { + java.util.Map.Entry entry = (java.util.Map.Entry) i.next(); + key = (String) entry.getKey(); + if (key.indexOf(type) != -1) { //如果匹配则删除掉 + k++; + } + } + } catch (Exception ex) { + ex.printStackTrace(); + } + + return k; + } + + //获取缓存对象中的所有键值名称 + public static ArrayList getCacheAllkey() { + ArrayList a = new ArrayList(); + try { + Iterator i = cacheMap.entrySet().iterator(); + while (i.hasNext()) { + java.util.Map.Entry entry = (java.util.Map.Entry) i.next(); + a.add((String) entry.getKey()); + } + } catch (Exception ex) {} finally { + return a; + } + } + + //获取缓存对象中指定类型 的键值名称 + public static ArrayList getCacheListkey(String type) { + ArrayList a = new ArrayList(); + String key; + try { + Iterator i = cacheMap.entrySet().iterator(); + while (i.hasNext()) { + java.util.Map.Entry entry = (java.util.Map.Entry) i.next(); + key = (String) entry.getKey(); + if (key.indexOf(type) != -1) { + a.add(key); + } + } + } catch (Exception ex) {} finally { + return a; + } + } + +} \ No newline at end of file diff --git a/ThunderSoft_repeatsubmitfilter/src/main/java/com/hq/LibhqFilterEnvironmentConfiguration.java b/ThunderSoft_repeatsubmitfilter/src/main/java/com/hq/LibhqFilterEnvironmentConfiguration.java new file mode 100644 index 000000000..830c2e292 --- /dev/null +++ b/ThunderSoft_repeatsubmitfilter/src/main/java/com/hq/LibhqFilterEnvironmentConfiguration.java @@ -0,0 +1,15 @@ +package com.hq; + +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; + +/** + * 加入spring环境配置(在spring.factories中指定) + */ +@Configuration +@ComponentScan(basePackageClasses = LibraryAutoScan.class) +public class LibhqFilterEnvironmentConfiguration { + public LibhqFilterEnvironmentConfiguration() { + System.out.println("LibhqFilterEnvironmentConfiguration"); + } +} diff --git a/ThunderSoft_repeatsubmitfilter/src/main/java/com/hq/LibraryAutoScan.java b/ThunderSoft_repeatsubmitfilter/src/main/java/com/hq/LibraryAutoScan.java new file mode 100644 index 000000000..8269e7526 --- /dev/null +++ b/ThunderSoft_repeatsubmitfilter/src/main/java/com/hq/LibraryAutoScan.java @@ -0,0 +1,8 @@ +package com.hq; + +/** + * 依赖库自动扫描类 + * @author system + */ +public class LibraryAutoScan { +} diff --git a/ThunderSoft_repeatsubmitfilter/src/main/java/com/hq/filter/RequestWrapper.java b/ThunderSoft_repeatsubmitfilter/src/main/java/com/hq/filter/RequestWrapper.java new file mode 100644 index 000000000..5625b4101 --- /dev/null +++ b/ThunderSoft_repeatsubmitfilter/src/main/java/com/hq/filter/RequestWrapper.java @@ -0,0 +1,63 @@ +package com.hq.filter; + +import org.springframework.util.StreamUtils; + +import javax.servlet.ReadListener; +import javax.servlet.ServletInputStream; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletRequestWrapper; +import java.io.BufferedReader; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; + +public class RequestWrapper extends HttpServletRequestWrapper { + private final byte[] body; + + public RequestWrapper(HttpServletRequest request) throws IOException { + super(request); + //保存一份InputStream,将其转换为字节数组 + body = StreamUtils.copyToByteArray(request.getInputStream()); + } + + //转换成String + public String getBodyString(){ + return new String(body, StandardCharsets.UTF_8); + } + + @Override + public BufferedReader getReader() throws IOException { + return new BufferedReader(new InputStreamReader(getInputStream())); + } + + //把保存好的InputStream,传下去 + @Override + public ServletInputStream getInputStream() throws IOException { + + final ByteArrayInputStream bais = new ByteArrayInputStream(body); + + return new ServletInputStream() { + + @Override + public int read() throws IOException { + return bais.read(); + } + + @Override + public boolean isFinished() { + return false; + } + + @Override + public boolean isReady() { + return false; + } + + @Override + public void setReadListener(ReadListener readListener) { + } + }; + } +} + diff --git a/ThunderSoft_repeatsubmitfilter/src/main/java/com/hq/filter/RestartSubmitFilter.java b/ThunderSoft_repeatsubmitfilter/src/main/java/com/hq/filter/RestartSubmitFilter.java new file mode 100644 index 000000000..561a2bb6f --- /dev/null +++ b/ThunderSoft_repeatsubmitfilter/src/main/java/com/hq/filter/RestartSubmitFilter.java @@ -0,0 +1,103 @@ +package com.hq.filter; + +import com.cache.CacheManager; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Component; +import org.springframework.util.StringUtils; + +import javax.servlet.*; +import javax.servlet.http.Cookie; +import javax.servlet.http.HttpServletRequest; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; + + +@Component +public class RestartSubmitFilter implements Filter { + + public static final String LOGIC_IDENTIFIER_SEPARATOR = ":"; + private Logger logger = LoggerFactory.getLogger(RestartSubmitFilter.class); + + + @Override + public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { + HttpServletRequest request = (HttpServletRequest) servletRequest; + String requestURI = request.getRequestURI(); + logger.warn("requestURI========" + requestURI); + if (!StringUtils.isEmpty(requestURI) && requestURI.contains("/management") || requestURI.contains("/api/lcplogics/LoginUser") + || requestURI.contains("/upload/")||requestURI.contains("/system/getUser") + ||requestURI.contains("/nuims/nuims")||requestURI.contains("/system/config")||requestURI.contains("/m/login")||requestURI.contains("/m") + ||requestURI.contains("api/lcplogics/")) { + filterChain.doFilter(servletRequest, servletResponse); + return; + } + requestURI = request.getRemoteHost() + request.getRequestURI(); + Cookie[] cookies = request.getCookies(); + String value = null; + logger.warn("cookies========" + cookies.toString()); + for (Cookie cookie : cookies) { + String name = cookie.getName(); + if ("authorization".equals(name)) { + value = cookie.getValue(); + break; + } + } + logger.warn("authorization========" + value); + String method = request.getMethod(); + logger.warn("method========" + method); + RequestWrapper requestWrapper = new RequestWrapper(request); + String bodyString = requestWrapper.getBodyString(); + logger.warn("bodyString========" + bodyString); + String logicIdentifier = requestURI + LOGIC_IDENTIFIER_SEPARATOR + method + LOGIC_IDENTIFIER_SEPARATOR + value + LOGIC_IDENTIFIER_SEPARATOR + bodyString; + boolean bool = CacheManager.setSimpleFlag(logicIdentifier, 1); + if (bool) { + logger.info("filter in"); + filterChain.doFilter(requestWrapper, servletResponse); + CacheManager.clearOnly(logicIdentifier); + logger.info("filter out"); + return; + } + logger.warn("filter ===============Repeated submission intercepted"); + } + + //获取Request的body数据 + private String getBody(ServletRequest request) { + StringBuilder stringBuilder = new StringBuilder(); + BufferedReader bufferedReader = null; + InputStream inputStream = null; + try { + inputStream = request.getInputStream(); + if (inputStream != null) { + bufferedReader = new BufferedReader(new InputStreamReader(inputStream)); + char[] charBuffer = new char[128]; + int bytesRead = -1; + while ((bytesRead = bufferedReader.read(charBuffer)) > 0) { + stringBuilder.append(charBuffer, 0, bytesRead); + } + } else { + stringBuilder.append(""); + } + } catch (IOException ex) { + + } finally { + if (inputStream != null) { + try { + inputStream.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + if (bufferedReader != null) { + try { + bufferedReader.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + return stringBuilder.toString(); + } +} diff --git a/ThunderSoft_repeatsubmitfilter/src/main/java/com/hq/filter/Temp.java b/ThunderSoft_repeatsubmitfilter/src/main/java/com/hq/filter/Temp.java new file mode 100644 index 000000000..a8282b8b8 --- /dev/null +++ b/ThunderSoft_repeatsubmitfilter/src/main/java/com/hq/filter/Temp.java @@ -0,0 +1,10 @@ +package com.hq.filter; + +import com.netease.lowcode.core.annotation.NaslStructure; + +/** + * 由于打包必须有nasl标签,所以创建临时类。若项目中有nasl标签的类,则不需要此类。 + */ +@NaslStructure +public class Temp { +} diff --git a/ThunderSoft_repeatsubmitfilter/src/main/resources/META-INF/spring.factories b/ThunderSoft_repeatsubmitfilter/src/main/resources/META-INF/spring.factories new file mode 100644 index 000000000..371e1dda6 --- /dev/null +++ b/ThunderSoft_repeatsubmitfilter/src/main/resources/META-INF/spring.factories @@ -0,0 +1 @@ +org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.hq.LibhqFilterEnvironmentConfiguration \ No newline at end of file