diff --git a/pom.xml b/pom.xml
index 7ce2cb8..29cf8ab 100644
--- a/pom.xml
+++ b/pom.xml
@@ -86,7 +86,6 @@
org.springframework.boot
spring-boot-starter-data-redis
- true
diff --git a/src/main/java/com/taoyuanx/securitydemo/config/GlobalConfig.java b/src/main/java/com/taoyuanx/securitydemo/config/GlobalConfig.java
index c9f1e60..9aa8bf3 100644
--- a/src/main/java/com/taoyuanx/securitydemo/config/GlobalConfig.java
+++ b/src/main/java/com/taoyuanx/securitydemo/config/GlobalConfig.java
@@ -4,17 +4,12 @@ import com.taoyuanx.securitydemo.security.ratelimit.AbstractRateLimiter;
import com.taoyuanx.securitydemo.security.ratelimit.RedisRateLimiter;
import lombok.Data;
import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
-import org.springframework.data.redis.connection.RedisConnectionFactory;
-import org.springframework.data.redis.core.ReactiveStringRedisTemplate;
-import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
-import java.net.UnknownHostException;
import java.util.List;
/**
@@ -40,19 +35,22 @@ public class GlobalConfig {
private String fileStorageDir;
private String systemFileFormat;
+
public String getConfig(String configKey) {
return environment.getProperty(configKey);
}
/**
* 限流实现类
+ *
* @param redisTemplate
* @return
*/
@Bean
- public AbstractRateLimiter rateLimiter(RedisTemplate redisTemplate){
- RedisRateLimiter redisRateLimiter=new RedisRateLimiter(redisTemplate);
- return redisRateLimiter;
+ @Autowired
+ public AbstractRateLimiter rateLimiter(StringRedisTemplate redisTemplate) {
+ RedisRateLimiter redisRateLimiter = new RedisRateLimiter(redisTemplate);
+ return redisRateLimiter;
}
diff --git a/src/main/java/com/taoyuanx/securitydemo/config/MvcConfig.java b/src/main/java/com/taoyuanx/securitydemo/config/MvcConfig.java
index 6530228..e54b3f3 100644
--- a/src/main/java/com/taoyuanx/securitydemo/config/MvcConfig.java
+++ b/src/main/java/com/taoyuanx/securitydemo/config/MvcConfig.java
@@ -154,6 +154,7 @@ public class MvcConfig implements WebMvcConfigurer {
}
@Bean
+
public FileHandler fileHandler(){
FileHandler fileHandler=new FileHandler(globalConfig.getFileStorageDir(),globalConfig.getTokenKey(),false,globalConfig.getSystemFileFormat());
return fileHandler;
diff --git a/src/main/java/com/taoyuanx/securitydemo/controller/BussinessController.java b/src/main/java/com/taoyuanx/securitydemo/controller/BussinessController.java
index 87401f0..cf1df85 100644
--- a/src/main/java/com/taoyuanx/securitydemo/controller/BussinessController.java
+++ b/src/main/java/com/taoyuanx/securitydemo/controller/BussinessController.java
@@ -9,6 +9,7 @@ import com.taoyuanx.securitydemo.dto.AccountDTO;
import com.taoyuanx.securitydemo.exception.ServiceException;
import com.taoyuanx.securitydemo.helper.ToeknHelper;
import com.taoyuanx.securitydemo.security.*;
+import com.taoyuanx.securitydemo.utils.CookieUtil;
import com.taoyuanx.securitydemo.utils.FileHandler;
import com.taoyuanx.securitydemo.utils.FileTypeCheckUtil;
import com.taoyuanx.securitydemo.utils.PasswordUtil;
@@ -156,6 +157,19 @@ public class BussinessController {
}
+
+ /**
+ * 登录安全控制
+ *
+ * @return
+ */
+ @PostMapping("loginOut")
+ @ResponseBody
+ public void loginOut(HttpServletResponse response, HttpServletRequest request) throws Exception {
+ CookieUtil.removeCookie(response, "/", SystemConstants.TOKEN_COOKIE_KEY);
+ request.getSession().invalidate();
+ }
+
/**
* 黑名单测试
*
@@ -178,7 +192,7 @@ public class BussinessController {
*/
String fileId = DateUtil.format(new Date(), "yyyy-mm-dd") + "/" + multipartFile.getOriginalFilename();
File file = new File(globalConfig.getFileStorageDir(), fileId);
- if(!file.getParentFile().exists()){
+ if (!file.getParentFile().exists()) {
file.getParentFile().mkdirs();
}
multipartFile.transferTo(file);
diff --git a/src/main/java/com/taoyuanx/securitydemo/interceptor/TokenAuthHandlerIntercepter.java b/src/main/java/com/taoyuanx/securitydemo/interceptor/TokenAuthHandlerIntercepter.java
index 6f0ee25..db11412 100644
--- a/src/main/java/com/taoyuanx/securitydemo/interceptor/TokenAuthHandlerIntercepter.java
+++ b/src/main/java/com/taoyuanx/securitydemo/interceptor/TokenAuthHandlerIntercepter.java
@@ -68,10 +68,11 @@ public class TokenAuthHandlerIntercepter implements HandlerInterceptor {
return false;
}
Map tokenData = toeknHelper.vafy(token);
- Integer tokenAccountId = (Integer) tokenData.get(SystemConstants.TOKEN_ACCOUNTID_KEY);
- if (tokenAccountId.longValue() != accountId) {
+ Long tokenAccountId = toeknHelper.getAccountId(tokenData);
+ if (tokenAccountId != accountId) {
return false;
}
+
return true;
}
if (null != publicUrl && publicUrl.size() > 0) {
diff --git a/src/main/java/com/taoyuanx/securitydemo/security/RateLimit.java b/src/main/java/com/taoyuanx/securitydemo/security/RateLimit.java
index e9c578a..ba04cd2 100644
--- a/src/main/java/com/taoyuanx/securitydemo/security/RateLimit.java
+++ b/src/main/java/com/taoyuanx/securitydemo/security/RateLimit.java
@@ -14,10 +14,12 @@ public @interface RateLimit {
* limit 每秒并发
* key 限流的key
* type 限流类型 参见:com.taoyuanx.securitydemo.security.RateLimitType
+ * totalCount 次数限流
*/
double limit() default 100;
String key() default "" ;
RateLimitType type() default RateLimitType.METHOD;
+ long totalCount() default 0;
}
\ No newline at end of file
diff --git a/src/main/java/com/taoyuanx/securitydemo/security/RateLimitType.java b/src/main/java/com/taoyuanx/securitydemo/security/RateLimitType.java
index a4c2c0a..4066288 100644
--- a/src/main/java/com/taoyuanx/securitydemo/security/RateLimitType.java
+++ b/src/main/java/com/taoyuanx/securitydemo/security/RateLimitType.java
@@ -8,7 +8,8 @@ package com.taoyuanx.securitydemo.security;
public enum RateLimitType {
IP(0, "IP限流"), METHOD(1, "方法名"),
SERVICE_KEY(3, "业务自定义key"),
- GLOBAL(4,"系统全局");
+ GLOBAL(4,"系统全局"),
+ TOTAL_COUNT(5,"总次数限制");
private int code;
private String desc;
@@ -31,6 +32,8 @@ public enum RateLimitType {
case 4:
return GLOBAL;
+ case 5:
+ return TOTAL_COUNT;
}
return null;
}
diff --git a/src/main/java/com/taoyuanx/securitydemo/security/ratelimit/AbstractRateLimiter.java b/src/main/java/com/taoyuanx/securitydemo/security/ratelimit/AbstractRateLimiter.java
index 3b897c7..398f621 100644
--- a/src/main/java/com/taoyuanx/securitydemo/security/ratelimit/AbstractRateLimiter.java
+++ b/src/main/java/com/taoyuanx/securitydemo/security/ratelimit/AbstractRateLimiter.java
@@ -4,6 +4,7 @@ package com.taoyuanx.securitydemo.security.ratelimit;
* @author dushitaoyuan
* @desc 抽象限流
* @date 2019/9/5
+ *
*/
public abstract class AbstractRateLimiter {
@@ -18,18 +19,16 @@ public abstract class AbstractRateLimiter {
public boolean tryAcquire(String key, Double limit){
return doTryAcquire(1,key,limit);
}
+ protected abstract boolean doTryAcquire(int permits, String key, Double limit);
/**
- * 尝试获取令牌
- *
- * @param permits 获取令牌数量
- * @param key 限流标识
- * @param limit 限流速率
+ * 增加资源访问次数 用户可自行持久化记录
+ * @param count
+ * @param key
+ * @param totalCount
* @return
*/
- public boolean tryAcquire(int permits, String key, Double limit){
- return doTryAcquire(permits,key,limit);
- }
+ public abstract boolean tryCount(int count,String key,Long totalCount);
+
- protected abstract boolean doTryAcquire(int permits, String key, Double limit);
}
diff --git a/src/main/java/com/taoyuanx/securitydemo/security/ratelimit/GuavaRateLimiter.java b/src/main/java/com/taoyuanx/securitydemo/security/ratelimit/GuavaRateLimiter.java
index af851cc..f4339e8 100644
--- a/src/main/java/com/taoyuanx/securitydemo/security/ratelimit/GuavaRateLimiter.java
+++ b/src/main/java/com/taoyuanx/securitydemo/security/ratelimit/GuavaRateLimiter.java
@@ -1,9 +1,13 @@
package com.taoyuanx.securitydemo.security.ratelimit;
-import com.google.common.collect.Maps;
+import com.google.common.hash.BloomFilter;
+import com.google.common.hash.Funnels;
import com.google.common.util.concurrent.RateLimiter;
+import java.nio.charset.Charset;
import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.LongAdder;
/**
* @author dushitaoyuan
@@ -11,18 +15,15 @@ import java.util.Map;
* @date 2019/9/5
*/
public class GuavaRateLimiter extends AbstractRateLimiter {
- private Map rateHolder = Maps.newConcurrentMap();
private static final int MAX_HOLDER_SIZE = 50000;
- /* @Override
- public boolean tryAcquire(String key, Double limit) {
- return doTryAcquire(1, key, limit);
- }
+ private Map rateHolder = new ConcurrentHashMap<>(MAX_HOLDER_SIZE);
+
+ private Map countHolder = new ConcurrentHashMap();
+ /**
+ * 总数限流到0后,标记
+ */
+ private BloomFilter TOTAL_LIMIT_ZERO_FLAG = BloomFilter.create(Funnels.stringFunnel(Charset.defaultCharset()), MAX_HOLDER_SIZE * 20);
- @Override
- public boolean tryAcquire(int permits, String key, Double limit) {
- return doTryAcquire(permits, key, limit);
- }
-*/
protected boolean doTryAcquire(int permits, String key, Double limit) {
//超过固定阈值,清空,重构
if (rateHolder.size() > MAX_HOLDER_SIZE) {
@@ -37,5 +38,36 @@ public class GuavaRateLimiter extends AbstractRateLimiter {
return rateLimiter.tryAcquire(permits);
}
+ @Override
+ public boolean tryCount(int count, String key, Long totalCount) {
+ //标记后,直接返回false
+ if (TOTAL_LIMIT_ZERO_FLAG.mightContain(key)) {
+ return false;
+ }
+ //超过固定阈值,清空,重构 防止内存溢出
+ if (countHolder.size() > MAX_HOLDER_SIZE) {
+ countHolder.clear();
+ }
+ LongAdder longAdder = null;
+ if (countHolder.containsKey(key)) {
+ longAdder = countHolder.get(key);
+ longAdder.add(-count);
+ //资源总数用完后,标记
+ if (longAdder.longValue() <= 0) {
+ TOTAL_LIMIT_ZERO_FLAG.put(key);
+ countHolder.remove(key);
+ return true;
+ }
+ return false;
+ }
+ if (count > totalCount) {
+ return false;
+ }
+ longAdder = new LongAdder();
+ countHolder.putIfAbsent(key, longAdder);
+ countHolder.get(key).add(count);
+ return true;
+ }
+
}
diff --git a/src/main/java/com/taoyuanx/securitydemo/security/ratelimit/RedisRateLimiter.java b/src/main/java/com/taoyuanx/securitydemo/security/ratelimit/RedisRateLimiter.java
index 0fb0815..e4b6ecb 100644
--- a/src/main/java/com/taoyuanx/securitydemo/security/ratelimit/RedisRateLimiter.java
+++ b/src/main/java/com/taoyuanx/securitydemo/security/ratelimit/RedisRateLimiter.java
@@ -1,7 +1,7 @@
package com.taoyuanx.securitydemo.security.ratelimit;
import org.springframework.core.io.ClassPathResource;
-import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.data.redis.core.script.RedisScript;
import org.springframework.scripting.support.ResourceScriptSource;
@@ -17,40 +17,40 @@ import java.util.List;
* @date 2019/9/5
*/
public class RedisRateLimiter extends AbstractRateLimiter {
- private RedisTemplate stringRedisTemplate;
- private RedisScript> script;
+ private StringRedisTemplate redisTemplate;
+ private RedisScript> tokenScript;
+ private RedisScript countScript;
- public RedisRateLimiter(RedisTemplate redisTemplate) {
+ public RedisRateLimiter(StringRedisTemplate redisTemplate) {
DefaultRedisScript script = new DefaultRedisScript();
- script.setScriptSource(new ResourceScriptSource(
- new ClassPathResource("META-INF/demo.lua")));
+ script.setScriptSource(new ResourceScriptSource(new ClassPathResource("META-INF/rate_limiter_token.lua")));
script.setResultType(List.class);
- this.script = script;
- this.stringRedisTemplate = redisTemplate;
+ this.tokenScript = script;
+
+ script = new DefaultRedisScript();
+ script.setScriptSource(new ResourceScriptSource(new ClassPathResource("META-INF/rate_limiter_count.lua")));
+ script.setResultType(Long.class);
+ this.countScript = script;
+
+ this.redisTemplate = redisTemplate;
}
- /* @Override
- public boolean tryAcquire(String key, Double limit) {
- return doTryAcquire(1, key, limit);
- }
-
- @Override
- public boolean tryAcquire(int permits, String key, Double limit) {
- return doTryAcquire(permits, key, limit);
- }*/
@Override
protected boolean doTryAcquire(int permits, String key, Double limit) {
- List