Quellcode durchsuchen

优化防火墙处理逻辑;

Woody vor 2 Monaten
Ursprung
Commit
3f8c49715a

+ 0 - 14
framework-base/src/main/java/com/chelvc/framework/base/logic/ContextVariableParser.java

@@ -16,11 +16,6 @@ import lombok.NonNull;
  * @date 2025/2/23
  */
 public class ContextVariableParser implements VariableParser {
-    /**
-     * 上下文参数变量解析器实例
-     */
-    private static final ContextVariableParser INSTANCE = new ContextVariableParser();
-
     /**
      * 请求参数变量前缀
      */
@@ -37,15 +32,6 @@ public class ContextVariableParser implements VariableParser {
         this.initialize();
     }
 
-    /**
-     * 获取上下文参数变量解析器实例
-     *
-     * @return 上下文参数变量解析器实例
-     */
-    public static ContextVariableParser getInstance() {
-        return INSTANCE;
-    }
-
     /**
      * 初始化解析器
      */

+ 12 - 2
framework-security/src/main/java/com/chelvc/framework/security/config/SecurityConfigurer.java

@@ -1,12 +1,16 @@
 package com.chelvc.framework.security.config;
 
 import java.time.Duration;
+import java.util.List;
 import java.util.Set;
 
+import com.chelvc.framework.base.logic.ContextVariableParser;
+import com.chelvc.framework.base.logic.VariableParser;
 import com.chelvc.framework.security.annotation.Crypto;
 import com.chelvc.framework.security.crypto.CacheableSecurityCipherHandler;
 import com.chelvc.framework.security.crypto.DefaultSecurityCipherHandler;
 import com.chelvc.framework.security.crypto.SecurityCipherHandler;
+import com.chelvc.framework.security.firewall.Action;
 import com.chelvc.framework.security.firewall.DefaultFirewallProcessor;
 import com.chelvc.framework.security.firewall.FirewallProcessor;
 import com.chelvc.framework.security.interceptor.MethodSecurityExpression;
@@ -115,10 +119,16 @@ public class SecurityConfigurer extends GlobalMethodSecurityConfiguration implem
         });
     }
 
+    @Bean
+    @ConditionalOnMissingBean(VariableParser.class)
+    public VariableParser variableParser() {
+        return new ContextVariableParser();
+    }
+
     @Bean
     @ConditionalOnMissingBean(FirewallProcessor.class)
-    public FirewallProcessor firewallProcessor() {
-        return new DefaultFirewallProcessor();
+    public FirewallProcessor firewallProcessor(VariableParser parser, List<Action> actions) {
+        return new DefaultFirewallProcessor(parser, actions);
     }
 
     @Bean

+ 24 - 0
framework-security/src/main/java/com/chelvc/framework/security/firewall/Action.java

@@ -0,0 +1,24 @@
+package com.chelvc.framework.security.firewall;
+
+/**
+ * 动作接口
+ *
+ * @author Woody
+ * @date 2025/3/14
+ */
+public interface Action {
+    /**
+     * 获取动作名称
+     *
+     * @return 动作名称
+     */
+    String getName();
+
+    /**
+     * 执行动作
+     *
+     * @param principal 主体信息
+     * @param rule      匹配规则
+     */
+    void execute(String principal, Rule rule);
+}

+ 19 - 26
framework-security/src/main/java/com/chelvc/framework/security/firewall/DefaultFirewallProcessor.java

@@ -6,23 +6,20 @@ import java.util.List;
 import java.util.Objects;
 import java.util.concurrent.TimeUnit;
 
-import com.chelvc.framework.base.context.ApplicationContextHolder;
 import com.chelvc.framework.base.context.ThreadContextHolder;
-import com.chelvc.framework.base.logic.ContextVariableParser;
 import com.chelvc.framework.base.logic.VariableParser;
-import com.chelvc.framework.common.exception.FrameworkException;
 import com.chelvc.framework.common.model.Pair;
 import com.chelvc.framework.common.util.IdentityUtils;
 import com.chelvc.framework.common.util.ObjectUtils;
 import com.chelvc.framework.common.util.StringUtils;
 import com.chelvc.framework.redis.context.RedisContextHolder;
 import com.google.common.collect.Lists;
+import lombok.NonNull;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.dao.DataAccessException;
 import org.springframework.data.redis.core.RedisOperations;
 import org.springframework.data.redis.core.RedisTemplate;
 import org.springframework.data.redis.core.SessionCallback;
-import org.springframework.http.HttpStatus;
 
 /**
  * 防火墙处理器默认实现
@@ -32,14 +29,21 @@ import org.springframework.http.HttpStatus;
  */
 @Slf4j
 public class DefaultFirewallProcessor implements FirewallProcessor {
+    private final VariableParser parser;
+    private final List<Action> actions;
     private volatile IdentityUtils.Generator sequenceGenerator;
 
+    public DefaultFirewallProcessor(@NonNull VariableParser parser, @NonNull List<Action> actions) {
+        this.parser = parser;
+        this.actions = Collections.unmodifiableList(actions);
+    }
+
     /**
      * 获取序列号
      *
      * @return 序列号数字
      */
-    protected Long getSequence() {
+    private Long getSequence() {
         if (this.sequenceGenerator == null) {
             synchronized (this) {
                 if (this.sequenceGenerator == null) {
@@ -51,26 +55,16 @@ public class DefaultFirewallProcessor implements FirewallProcessor {
     }
 
     /**
-     * 获取变量解析器
-     *
-     * @return 变量解析器实例
-     */
-    protected VariableParser getVariableParser() {
-        return ContextVariableParser.getInstance();
-    }
-
-    /**
-     * 执行规则动作处理
+     * 处理规则动作
      *
      * @param principal 主体信息
      * @param rule      匹配规则
      */
-    protected void doAction(String principal, Rule rule) {
-        if (Objects.equals(rule.getAction(), "OBSERVE")) {
-            log.warn("Firewall rule triggered: {}, principal: {}", rule.getId(), principal);
-        } else if (Objects.equals(rule.getAction(), "INTERCEPT")) {
-            throw new FrameworkException(HttpStatus.FORBIDDEN.name(), null,
-                    ApplicationContextHolder.getMessage("Forbidden"));
+    private void doAction(String principal, Rule rule) {
+        for (Action action : this.actions) {
+            if (Objects.equals(rule.getAction(), action.getName())) {
+                action.execute(principal, rule);
+            }
         }
     }
 
@@ -81,12 +75,11 @@ public class DefaultFirewallProcessor implements FirewallProcessor {
      * @return 主体/规则列表
      */
     private List<Pair<String, Rule>> getMatchRules(List<Rule> rules) {
-        VariableParser parser = this.getVariableParser();
         List<Pair<String, Rule>> matches = Lists.newLinkedList();
         for (Rule rule : rules) {
             CharSequence principal = null;
-            if (rule.getCondition().eval(parser) && (rule.getCount() < 1
-                    || StringUtils.notEmpty(principal = rule.getPrincipal().parse(parser)))) {
+            if (rule.getCondition().eval(this.parser) && (rule.getCount() < 1
+                    || StringUtils.notEmpty(principal = rule.getPrincipal().parse(this.parser)))) {
                 matches.add(Pair.of(StringUtils.toString(principal), rule));
             }
         }
@@ -99,7 +92,7 @@ public class DefaultFirewallProcessor implements FirewallProcessor {
      * @param pairs 主体/规则列表
      * @return 动作列表
      */
-    private List<String> getAccessControls(List<Pair<String, Rule>> pairs) {
+    protected List<String> getAccessControls(List<Pair<String, Rule>> pairs) {
         List<String> keys = Lists.newArrayListWithCapacity(pairs.size());
         pairs.forEach(pair -> keys.add("acl:" + pair.getKey() + ":" + pair.getValue().getId()));
         RedisTemplate<String, String> template = RedisContextHolder.getDefaultTemplate();
@@ -111,7 +104,7 @@ public class DefaultFirewallProcessor implements FirewallProcessor {
      *
      * @param pairs 主体/规则列表
      */
-    private void refreshAccessControls(List<Pair<String, Rule>> pairs) {
+    protected void refreshAccessControls(List<Pair<String, Rule>> pairs) {
         // 批量刷新时间窗口数据
         Long sequence = this.getSequence(), timestamp = System.currentTimeMillis();
         RedisTemplate<String, Object> template = RedisContextHolder.getDefaultTemplate();

+ 31 - 0
framework-security/src/main/java/com/chelvc/framework/security/firewall/InterceptAction.java

@@ -0,0 +1,31 @@
+package com.chelvc.framework.security.firewall;
+
+import com.chelvc.framework.base.context.ApplicationContextHolder;
+import com.chelvc.framework.common.exception.FrameworkException;
+import org.springframework.http.HttpStatus;
+import org.springframework.stereotype.Component;
+
+/**
+ * 拦截动作实现
+ *
+ * @author Woody
+ * @date 2025/3/14
+ */
+@Component
+public class InterceptAction implements Action {
+    /**
+     * 动作名称
+     */
+    public static final String NAME = "INTERCEPT";
+
+    @Override
+    public String getName() {
+        return NAME;
+    }
+
+    @Override
+    public void execute(String principal, Rule rule) {
+        String message = ApplicationContextHolder.getMessage("Forbidden");
+        throw new FrameworkException(HttpStatus.FORBIDDEN.name(), null, message);
+    }
+}

+ 29 - 0
framework-security/src/main/java/com/chelvc/framework/security/firewall/ObserveAction.java

@@ -0,0 +1,29 @@
+package com.chelvc.framework.security.firewall;
+
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Component;
+
+/**
+ * 观察动作实现
+ *
+ * @author Woody
+ * @date 2025/3/14
+ */
+@Slf4j
+@Component
+public class ObserveAction implements Action {
+    /**
+     * 动作名称
+     */
+    public static final String NAME = "OBSERVE";
+
+    @Override
+    public String getName() {
+        return NAME;
+    }
+
+    @Override
+    public void execute(String principal, Rule rule) {
+        log.warn("Firewall rule triggered: {}, principal: {}", rule.getId(), principal);
+    }
+}

+ 26 - 19
framework-security/src/main/java/com/chelvc/framework/security/interceptor/SecurityFirewallInterceptor.java

@@ -95,6 +95,23 @@ public class SecurityFirewallInterceptor implements HandlerInterceptor, WebMvcCo
         return ApplicationContextHolder.getProperty("security.firewall.blacklists", Ip::host2ips);
     }
 
+    /**
+     * 获取客户端IP数字
+     *
+     * @return IP数字
+     */
+    private long getHostNumber() {
+        String host = SessionContextHolder.getHost();
+        if (StringUtils.notEmpty(host)) {
+            try {
+                return Ip.getAddressNumber(host);
+            } catch (Exception e) {
+                log.error("Address number convert failed", e);
+            }
+        }
+        return 0;
+    }
+
     /**
      * 判断规则是否匹配
      *
@@ -117,30 +134,20 @@ public class SecurityFirewallInterceptor implements HandlerInterceptor, WebMvcCo
             return true;
         }
 
-        String host = SessionContextHolder.getHost();
-        List<Ip> whitelists = this.getWhitelists(), blacklists = this.getBlacklists();
-        if (StringUtils.notEmpty(host) && (ObjectUtils.notEmpty(whitelists) || ObjectUtils.notEmpty(blacklists))) {
-            // 获取ip数字
-            long number;
-            try {
-                number = Ip.getAddressNumber(host);
-            } catch (Exception e) {
-                log.error("Address number convert failed", e);
-                return true;
-            }
-
+        long host = this.getHostNumber();
+        if (host > 0) {
             // 如果是IP白名单则放行
-            for (Ip ip : whitelists) {
-                if (ip.matches(number)) {
+            for (Ip ip : this.getWhitelists()) {
+                if (ip.matches(host)) {
                     return true;
                 }
             }
 
-            // 如果是IP黑单则拒绝访问
-            for (Ip ip : blacklists) {
-                if (ip.matches(number)) {
-                    throw new FrameworkException(HttpStatus.FORBIDDEN.name(), null,
-                            ApplicationContextHolder.getMessage("Forbidden"));
+            // 如果是IP黑名单则拒绝访问
+            for (Ip ip : this.getBlacklists()) {
+                if (ip.matches(host)) {
+                    String message = ApplicationContextHolder.getMessage("Forbidden");
+                    throw new FrameworkException(HttpStatus.FORBIDDEN.name(), null, message);
                 }
             }
         }