Kaynağa Gözat

网关增强

mrbird 6 yıl önce
ebeveyn
işleme
b1cf8844a4
61 değiştirilmiş dosya ile 2294 ekleme ve 214 silme
  1. 21 6
      febs-gateway/pom.xml
  2. 2 2
      febs-gateway/src/main/java/cc/mrbird/febs/gateway/common/configure/FebsGatewayErrorConfigure.java
  3. 1 1
      febs-gateway/src/main/java/cc/mrbird/febs/gateway/common/controller/FallbackController.java
  4. 59 0
      febs-gateway/src/main/java/cc/mrbird/febs/gateway/common/filter/FebsGatewayRequestFilter.java
  5. 1 4
      febs-gateway/src/main/java/cc/mrbird/febs/gateway/common/handler/FebsGatewayExceptionHandler.java
  6. 1 1
      febs-gateway/src/main/java/cc/mrbird/febs/gateway/common/properties/FebsGatewayProperties.java
  7. 1 1
      febs-gateway/src/main/java/cc/mrbird/febs/gateway/common/runner/StartedUpRunner.java
  8. 0 88
      febs-gateway/src/main/java/cc/mrbird/febs/gateway/configure/FebsGatewaySentinelConfigure.java
  9. 42 0
      febs-gateway/src/main/java/cc/mrbird/febs/gateway/enhance/auth/AuthenticationManager.java
  10. 73 0
      febs-gateway/src/main/java/cc/mrbird/febs/gateway/enhance/auth/JWTTokenHelper.java
  11. 53 0
      febs-gateway/src/main/java/cc/mrbird/febs/gateway/enhance/auth/SecurityConfigure.java
  12. 43 0
      febs-gateway/src/main/java/cc/mrbird/febs/gateway/enhance/auth/SecurityContextRepository.java
  13. 42 0
      febs-gateway/src/main/java/cc/mrbird/febs/gateway/enhance/auth/WebFluxSecurityCorsConfigure.java
  14. 45 0
      febs-gateway/src/main/java/cc/mrbird/febs/gateway/enhance/configure/FebsRouteEnhanceConfigure.java
  15. 55 0
      febs-gateway/src/main/java/cc/mrbird/febs/gateway/enhance/controller/BlackListController.java
  16. 40 0
      febs-gateway/src/main/java/cc/mrbird/febs/gateway/enhance/controller/BlockLogController.java
  17. 40 0
      febs-gateway/src/main/java/cc/mrbird/febs/gateway/enhance/controller/RateLimitLogController.java
  18. 54 0
      febs-gateway/src/main/java/cc/mrbird/febs/gateway/enhance/controller/RateLimitRuleController.java
  19. 40 0
      febs-gateway/src/main/java/cc/mrbird/febs/gateway/enhance/controller/RouteLogController.java
  20. 38 0
      febs-gateway/src/main/java/cc/mrbird/febs/gateway/enhance/controller/RouteLoginController.java
  21. 55 0
      febs-gateway/src/main/java/cc/mrbird/febs/gateway/enhance/controller/RouteUserController.java
  22. 59 0
      febs-gateway/src/main/java/cc/mrbird/febs/gateway/enhance/entity/BlackList.java
  23. 48 0
      febs-gateway/src/main/java/cc/mrbird/febs/gateway/enhance/entity/BlockLog.java
  24. 48 0
      febs-gateway/src/main/java/cc/mrbird/febs/gateway/enhance/entity/RateLimitLog.java
  25. 65 0
      febs-gateway/src/main/java/cc/mrbird/febs/gateway/enhance/entity/RateLimitRule.java
  26. 56 0
      febs-gateway/src/main/java/cc/mrbird/febs/gateway/enhance/entity/RouteLog.java
  27. 31 0
      febs-gateway/src/main/java/cc/mrbird/febs/gateway/enhance/entity/RouteUser.java
  28. 17 0
      febs-gateway/src/main/java/cc/mrbird/febs/gateway/enhance/mapper/BlackListMapper.java
  29. 13 0
      febs-gateway/src/main/java/cc/mrbird/febs/gateway/enhance/mapper/BlockLogMapper.java
  30. 12 0
      febs-gateway/src/main/java/cc/mrbird/febs/gateway/enhance/mapper/RateLimitLogMapper.java
  31. 14 0
      febs-gateway/src/main/java/cc/mrbird/febs/gateway/enhance/mapper/RateLimitRuleMapper.java
  32. 15 0
      febs-gateway/src/main/java/cc/mrbird/febs/gateway/enhance/mapper/RouteLogMapper.java
  33. 15 0
      febs-gateway/src/main/java/cc/mrbird/febs/gateway/enhance/mapper/RouteUserMapper.java
  34. 29 0
      febs-gateway/src/main/java/cc/mrbird/febs/gateway/enhance/runner/FebsRouteEnhanceRunner.java
  35. 24 0
      febs-gateway/src/main/java/cc/mrbird/febs/gateway/enhance/service/BlackListService.java
  36. 17 0
      febs-gateway/src/main/java/cc/mrbird/febs/gateway/enhance/service/BlockLogService.java
  37. 19 0
      febs-gateway/src/main/java/cc/mrbird/febs/gateway/enhance/service/RateLimitLogService.java
  38. 23 0
      febs-gateway/src/main/java/cc/mrbird/febs/gateway/enhance/service/RateLimitRuleService.java
  39. 34 0
      febs-gateway/src/main/java/cc/mrbird/febs/gateway/enhance/service/RouteEnhanceCacheService.java
  40. 25 0
      febs-gateway/src/main/java/cc/mrbird/febs/gateway/enhance/service/RouteEnhanceService.java
  41. 19 0
      febs-gateway/src/main/java/cc/mrbird/febs/gateway/enhance/service/RouteLogService.java
  42. 20 0
      febs-gateway/src/main/java/cc/mrbird/febs/gateway/enhance/service/RouteUserService.java
  43. 103 0
      febs-gateway/src/main/java/cc/mrbird/febs/gateway/enhance/service/impl/BlackListServiceImpl.java
  44. 79 0
      febs-gateway/src/main/java/cc/mrbird/febs/gateway/enhance/service/impl/BlockLogServiceImpl.java
  45. 79 0
      febs-gateway/src/main/java/cc/mrbird/febs/gateway/enhance/service/impl/RateLimitLogServiceImpl.java
  46. 101 0
      febs-gateway/src/main/java/cc/mrbird/febs/gateway/enhance/service/impl/RateLimitRuleServiceImpl.java
  47. 113 0
      febs-gateway/src/main/java/cc/mrbird/febs/gateway/enhance/service/impl/RouteEnhanceCacheServiceImpl.java
  48. 221 0
      febs-gateway/src/main/java/cc/mrbird/febs/gateway/enhance/service/impl/RouteEnhanceServiceImpl.java
  49. 84 0
      febs-gateway/src/main/java/cc/mrbird/febs/gateway/enhance/service/impl/RouteLogServiceImpl.java
  50. 82 0
      febs-gateway/src/main/java/cc/mrbird/febs/gateway/enhance/service/impl/RouteUserServiceImpl.java
  51. 58 0
      febs-gateway/src/main/java/cc/mrbird/febs/gateway/enhance/utils/AddressUtil.java
  52. 26 0
      febs-gateway/src/main/java/cc/mrbird/febs/gateway/enhance/utils/PageableExecutionUtil.java
  53. 30 0
      febs-gateway/src/main/java/cc/mrbird/febs/gateway/enhance/utils/RouteEnhanceCacheUtil.java
  54. 0 105
      febs-gateway/src/main/java/cc/mrbird/febs/gateway/filter/FebsGatewayRequestFilter.java
  55. BIN
      febs-gateway/src/main/resources/2879260_apicloud.mrbird.cn.pfx
  56. 4 1
      febs-gateway/src/main/resources/bootstrap.yml
  57. BIN
      febs-gateway/src/main/resources/ip2region/ip2region.db
  58. 1 1
      febs-server/febs-server-system/src/main/java/cc/mrbird/febs/server/system/helper/GeneratorHelper.java
  59. 1 1
      febs-server/febs-server-system/src/main/java/cc/mrbird/febs/server/system/service/impl/LogServiceImpl.java
  60. 1 1
      febs-server/febs-server-system/src/main/java/cc/mrbird/febs/server/system/service/impl/LoginLogServiceImpl.java
  61. 2 2
      febs-server/febs-server-system/src/main/java/cc/mrbird/febs/server/system/utils/AddressUtil.java

+ 21 - 6
febs-gateway/pom.xml

@@ -27,10 +27,6 @@
                     <groupId>com.baomidou</groupId>
                     <artifactId>mybatis-plus-boot-starter</artifactId>
                 </exclusion>
-                <exclusion>
-                    <groupId>org.springframework.boot</groupId>
-                    <artifactId>spring-boot-starter-data-redis</artifactId>
-                </exclusion>
                 <exclusion>
                     <groupId>org.springframework.cloud</groupId>
                     <artifactId>spring-cloud-starter-oauth2</artifactId>
@@ -58,9 +54,28 @@
             <groupId>org.springframework.boot</groupId>
             <artifactId>spring-boot-starter-actuator</artifactId>
         </dependency>
+
+        <!-- 网关增强依赖 -->
+        <dependency>
+            <groupId>org.apache.commons</groupId>
+            <artifactId>commons-pool2</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-webflux</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-security</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-data-mongodb-reactive</artifactId>
+        </dependency>
         <dependency>
-            <groupId>com.alibaba.csp</groupId>
-            <artifactId>sentinel-spring-cloud-gateway-adapter</artifactId>
+            <groupId>io.jsonwebtoken</groupId>
+            <artifactId>jjwt</artifactId>
+            <version>0.9.1</version>
         </dependency>
     </dependencies>
 

+ 2 - 2
febs-gateway/src/main/java/cc/mrbird/febs/gateway/configure/FebsGatewayErrorConfigure.java → febs-gateway/src/main/java/cc/mrbird/febs/gateway/common/configure/FebsGatewayErrorConfigure.java

@@ -1,6 +1,6 @@
-package cc.mrbird.febs.gateway.configure;
+package cc.mrbird.febs.gateway.common.configure;
 
-import cc.mrbird.febs.gateway.handler.FebsGatewayExceptionHandler;
+import cc.mrbird.febs.gateway.common.handler.FebsGatewayExceptionHandler;
 import org.springframework.beans.factory.ObjectProvider;
 import org.springframework.boot.autoconfigure.web.ResourceProperties;
 import org.springframework.boot.autoconfigure.web.ServerProperties;

+ 1 - 1
febs-gateway/src/main/java/cc/mrbird/febs/gateway/controller/FallbackController.java → febs-gateway/src/main/java/cc/mrbird/febs/gateway/common/controller/FallbackController.java

@@ -1,4 +1,4 @@
-package cc.mrbird.febs.gateway.controller;
+package cc.mrbird.febs.gateway.common.controller;
 
 import cc.mrbird.febs.common.entity.FebsResponse;
 import org.springframework.http.HttpStatus;

+ 59 - 0
febs-gateway/src/main/java/cc/mrbird/febs/gateway/common/filter/FebsGatewayRequestFilter.java

@@ -0,0 +1,59 @@
+package cc.mrbird.febs.gateway.common.filter;
+
+import cc.mrbird.febs.common.entity.constant.FebsConstant;
+import cc.mrbird.febs.gateway.common.properties.FebsGatewayProperties;
+import cc.mrbird.febs.gateway.enhance.service.RouteEnhanceService;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.cloud.gateway.filter.GatewayFilterChain;
+import org.springframework.cloud.gateway.filter.GlobalFilter;
+import org.springframework.core.annotation.Order;
+import org.springframework.http.server.reactive.ServerHttpRequest;
+import org.springframework.stereotype.Component;
+import org.springframework.util.AntPathMatcher;
+import org.springframework.util.Base64Utils;
+import org.springframework.web.server.ServerWebExchange;
+import reactor.core.publisher.Mono;
+
+/**
+ * @author MrBird
+ */
+@Slf4j
+@Component
+@Order(0)
+public class FebsGatewayRequestFilter implements GlobalFilter {
+
+    @Autowired
+    private FebsGatewayProperties properties;
+    @Autowired
+    private RouteEnhanceService routeEnhanceService;
+
+    @Value("${febs.gateway.enhance:false}")
+    private Boolean routeEhance;
+
+    private AntPathMatcher pathMatcher = new AntPathMatcher();
+
+    @Override
+    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
+        if (routeEhance) {
+            Mono<Void> balckListResult = routeEnhanceService.filterBalckList(exchange);
+            if (balckListResult != null) {
+                routeEnhanceService.saveBlockLogs(exchange);
+                return balckListResult;
+            }
+            Mono<Void> rateLimitResult = routeEnhanceService.filterRateLimit(exchange);
+            if (rateLimitResult != null) {
+                routeEnhanceService.saveRateLimitLogs(exchange);
+                return rateLimitResult;
+            }
+            routeEnhanceService.saveRequestLogs(exchange);
+        }
+
+        byte[] token = Base64Utils.encode((FebsConstant.GATEWAY_TOKEN_VALUE).getBytes());
+        String[] headerValues = {new String(token)};
+        ServerHttpRequest build = exchange.getRequest().mutate().header(FebsConstant.GATEWAY_TOKEN_HEADER, headerValues).build();
+        ServerWebExchange newExchange = exchange.mutate().request(build).build();
+        return chain.filter(newExchange);
+    }
+}

+ 1 - 4
febs-gateway/src/main/java/cc/mrbird/febs/gateway/handler/FebsGatewayExceptionHandler.java → febs-gateway/src/main/java/cc/mrbird/febs/gateway/common/handler/FebsGatewayExceptionHandler.java

@@ -1,6 +1,5 @@
-package cc.mrbird.febs.gateway.handler;
+package cc.mrbird.febs.gateway.common.handler;
 
-import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowException;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.commons.lang3.StringUtils;
 import org.springframework.boot.autoconfigure.web.ErrorProperties;
@@ -50,8 +49,6 @@ public class FebsGatewayExceptionHandler extends DefaultErrorWebExceptionHandler
         } else if (error instanceof ResponseStatusException
                 && StringUtils.containsIgnoreCase(error.getMessage(), HttpStatus.NOT_FOUND.toString())) {
             errorMessage = "未找到该资源";
-        } else if (error instanceof ParamFlowException) {
-            errorMessage = "访问频率超限";
         } else {
             errorMessage = "网关转发异常";
         }

+ 1 - 1
febs-gateway/src/main/java/cc/mrbird/febs/gateway/properties/FebsGatewayProperties.java → febs-gateway/src/main/java/cc/mrbird/febs/gateway/common/properties/FebsGatewayProperties.java

@@ -1,4 +1,4 @@
-package cc.mrbird.febs.gateway.properties;
+package cc.mrbird.febs.gateway.common.properties;
 
 import lombok.Data;
 import org.springframework.boot.SpringBootConfiguration;

+ 1 - 1
febs-gateway/src/main/java/cc/mrbird/febs/gateway/runner/StartedUpRunner.java → febs-gateway/src/main/java/cc/mrbird/febs/gateway/common/runner/StartedUpRunner.java

@@ -1,4 +1,4 @@
-package cc.mrbird.febs.gateway.runner;
+package cc.mrbird.febs.gateway.common.runner;
 
 import cc.mrbird.febs.common.entity.constant.FebsServerConstant;
 import lombok.extern.slf4j.Slf4j;

+ 0 - 88
febs-gateway/src/main/java/cc/mrbird/febs/gateway/configure/FebsGatewaySentinelConfigure.java

@@ -1,88 +0,0 @@
-package cc.mrbird.febs.gateway.configure;
-
-import com.alibaba.csp.sentinel.adapter.gateway.common.SentinelGatewayConstants;
-import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiDefinition;
-import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiPathPredicateItem;
-import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiPredicateItem;
-import com.alibaba.csp.sentinel.adapter.gateway.common.api.GatewayApiDefinitionManager;
-import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayFlowRule;
-import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayParamFlowItem;
-import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayRuleManager;
-import com.alibaba.csp.sentinel.adapter.gateway.sc.SentinelGatewayFilter;
-import com.alibaba.csp.sentinel.adapter.gateway.sc.exception.SentinelGatewayBlockExceptionHandler;
-import org.springframework.beans.factory.ObjectProvider;
-import org.springframework.cloud.gateway.filter.GlobalFilter;
-import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Configuration;
-import org.springframework.core.Ordered;
-import org.springframework.core.annotation.Order;
-import org.springframework.http.codec.ServerCodecConfigurer;
-import org.springframework.web.reactive.result.view.ViewResolver;
-
-import javax.annotation.PostConstruct;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-
-/**
- * @author MrBird
- */
-@Configuration
-public class FebsGatewaySentinelConfigure {
-    private final List<ViewResolver> viewResolvers;
-    private final ServerCodecConfigurer serverCodecConfigurer;
-
-    public FebsGatewaySentinelConfigure(ObjectProvider<List<ViewResolver>> viewResolversProvider,
-                                        ServerCodecConfigurer serverCodecConfigurer) {
-        this.viewResolvers = viewResolversProvider.getIfAvailable(Collections::emptyList);
-        this.serverCodecConfigurer = serverCodecConfigurer;
-    }
-
-    @Bean
-    @Order(Ordered.HIGHEST_PRECEDENCE)
-    public SentinelGatewayBlockExceptionHandler sentinelGatewayBlockExceptionHandler() {
-        return new SentinelGatewayBlockExceptionHandler(viewResolvers, serverCodecConfigurer);
-    }
-
-    @Bean
-    @Order(-1)
-    public GlobalFilter sentinelGatewayFilter() {
-        return new SentinelGatewayFilter();
-    }
-
-    @PostConstruct
-    public void doInit() {
-        initGatewayRules();
-    }
-
-    /**
-     * 验证码限流
-     */
-    private void initGatewayRules() {
-        Set<ApiDefinition> definitions = new HashSet<>();
-        Set<ApiPredicateItem> predicateItems = new HashSet<>();
-
-        predicateItems.add(new ApiPathPredicateItem().setPattern("/auth/captcha"));
-        ApiDefinition definition = new ApiDefinition("captcha")
-                .setPredicateItems(predicateItems);
-        definitions.add(definition);
-        GatewayApiDefinitionManager.loadApiDefinitions(definitions);
-
-        Set<GatewayFlowRule> rules = new HashSet<>();
-
-        rules.add(new GatewayFlowRule("captcha")
-                .setResourceMode(SentinelGatewayConstants.RESOURCE_MODE_CUSTOM_API_NAME)
-                .setParamItem(
-                        new GatewayParamFlowItem()
-                                .setParseStrategy(SentinelGatewayConstants.PARAM_PARSE_STRATEGY_URL_PARAM)
-                                .setFieldName("key")
-                                .setMatchStrategy(SentinelGatewayConstants.PARAM_MATCH_STRATEGY_EXACT)
-                                .setParseStrategy(SentinelGatewayConstants.PARAM_PARSE_STRATEGY_CLIENT_IP)
-                )
-                .setCount(10)
-                .setIntervalSec(60)
-        );
-        GatewayRuleManager.loadRules(rules);
-    }
-}

+ 42 - 0
febs-gateway/src/main/java/cc/mrbird/febs/gateway/enhance/auth/AuthenticationManager.java

@@ -0,0 +1,42 @@
+package cc.mrbird.febs.gateway.enhance.auth;
+
+import io.jsonwebtoken.Claims;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.authentication.ReactiveAuthenticationManager;
+import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.authority.AuthorityUtils;
+import org.springframework.stereotype.Component;
+import reactor.core.publisher.Mono;
+
+/**
+ * @author MrBird
+ */
+@Component
+public class AuthenticationManager implements ReactiveAuthenticationManager {
+
+    @Autowired
+    private JWTTokenHelper tokenHelper;
+
+    @Override
+    public Mono<Authentication> authenticate(Authentication authentication) {
+        String token = authentication.getCredentials().toString();
+        String username;
+        try {
+            username = tokenHelper.getUsernameFromToken(token);
+        } catch (Exception e) {
+            username = null;
+        }
+        if (StringUtils.isNotBlank(username) && tokenHelper.validateToken(token)) {
+            Claims claims = tokenHelper.getAllClaimsFromToken(token);
+            String permissions = claims.get("permission", String.class);
+            UsernamePasswordAuthenticationToken auth = new UsernamePasswordAuthenticationToken(username, null,
+                    AuthorityUtils.commaSeparatedStringToAuthorityList(permissions)
+            );
+            return Mono.just(auth);
+        } else {
+            return Mono.empty();
+        }
+    }
+}

+ 73 - 0
febs-gateway/src/main/java/cc/mrbird/febs/gateway/enhance/auth/JWTTokenHelper.java

@@ -0,0 +1,73 @@
+package cc.mrbird.febs.gateway.enhance.auth;
+
+import cc.mrbird.febs.gateway.enhance.entity.RouteUser;
+import io.jsonwebtoken.Claims;
+import io.jsonwebtoken.Jwts;
+import io.jsonwebtoken.SignatureAlgorithm;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
+
+import java.io.Serializable;
+import java.util.Base64;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @author MrBird
+ */
+@Component
+public class JWTTokenHelper implements Serializable {
+
+    private static final long serialVersionUID = 1579222883969867182L;
+
+    @Value("${febs.gateway.jwt.secret}")
+    private String secret;
+
+    @Value("${febs.gateway.jwt.expiration}")
+    private String expirationTime;
+
+    public Claims getAllClaimsFromToken(String token) {
+        return Jwts.parser()
+                .setSigningKey(Base64.getEncoder().encodeToString(secret.getBytes()))
+                .parseClaimsJws(token)
+                .getBody();
+    }
+
+    public String getUsernameFromToken(String token) {
+        return getAllClaimsFromToken(token).getSubject();
+    }
+
+    public Date getExpirationDateFromToken(String token) {
+        return getAllClaimsFromToken(token).getExpiration();
+    }
+
+    private Boolean isTokenExpired(String token) {
+        final Date expiration = getExpirationDateFromToken(token);
+        return expiration.before(new Date());
+    }
+
+    public String generateToken(RouteUser routeUser) {
+        Map<String, Object> claims = new HashMap<>();
+        claims.put("permission", routeUser.getRoles());
+        return doGenerateToken(claims, routeUser.getUsername());
+    }
+
+    private String doGenerateToken(Map<String, Object> claims, String username) {
+        long expirationTimeLong = Long.parseLong(expirationTime);
+
+        final Date createdDate = new Date();
+        final Date expirationDate = new Date(createdDate.getTime() + expirationTimeLong * 1000);
+        return Jwts.builder()
+                .setClaims(claims)
+                .setSubject(username)
+                .setIssuedAt(createdDate)
+                .setExpiration(expirationDate)
+                .signWith(SignatureAlgorithm.HS512, Base64.getEncoder().encodeToString(secret.getBytes()))
+                .compact();
+    }
+
+    public Boolean validateToken(String token) {
+        return !isTokenExpired(token);
+    }
+}

+ 53 - 0
febs-gateway/src/main/java/cc/mrbird/febs/gateway/enhance/auth/SecurityConfigure.java

@@ -0,0 +1,53 @@
+package cc.mrbird.febs.gateway.enhance.auth;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.http.HttpMethod;
+import org.springframework.http.HttpStatus;
+import org.springframework.security.config.annotation.method.configuration.EnableReactiveMethodSecurity;
+import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
+import org.springframework.security.config.web.server.ServerHttpSecurity;
+import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
+import org.springframework.security.crypto.password.PasswordEncoder;
+import org.springframework.security.web.server.SecurityWebFilterChain;
+import reactor.core.publisher.Mono;
+
+/**
+ * @author MrBird
+ */
+@Configuration
+@EnableWebFluxSecurity
+@EnableReactiveMethodSecurity
+public class SecurityConfigure {
+
+    @Autowired
+    private AuthenticationManager authenticationManager;
+
+    @Autowired
+    private SecurityContextRepository securityContextRepository;
+
+    @Bean
+    public PasswordEncoder passwordEncoder(){
+        return new BCryptPasswordEncoder();
+    }
+
+    @Bean
+    public SecurityWebFilterChain securitygWebFilterChain(ServerHttpSecurity http) {
+        return http
+                .exceptionHandling()
+                .authenticationEntryPoint((s, e) -> Mono.fromRunnable(() -> s.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED)))
+                .accessDeniedHandler((s, e) -> Mono.fromRunnable(() -> s.getResponse().setStatusCode(HttpStatus.FORBIDDEN)))
+                .and()
+                .csrf().disable()
+                .formLogin().disable()
+                .httpBasic().disable()
+                .authenticationManager(authenticationManager)
+                .securityContextRepository(securityContextRepository)
+                .authorizeExchange()
+                .pathMatchers(HttpMethod.OPTIONS).permitAll()
+                .pathMatchers("/route/auth/**").authenticated()
+                .anyExchange().permitAll()
+                .and().build();
+    }
+}

+ 43 - 0
febs-gateway/src/main/java/cc/mrbird/febs/gateway/enhance/auth/SecurityContextRepository.java

@@ -0,0 +1,43 @@
+package cc.mrbird.febs.gateway.enhance.auth;
+
+import cc.mrbird.febs.common.entity.constant.FebsConstant;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.server.reactive.ServerHttpRequest;
+import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.context.SecurityContext;
+import org.springframework.security.core.context.SecurityContextImpl;
+import org.springframework.security.web.server.context.ServerSecurityContextRepository;
+import org.springframework.stereotype.Component;
+import org.springframework.web.server.ServerWebExchange;
+import reactor.core.publisher.Mono;
+
+/**
+ * @author MrBird
+ */
+@Component
+public class SecurityContextRepository implements ServerSecurityContextRepository {
+
+    @Autowired
+    private AuthenticationManager authenticationManager;
+
+    @Override
+    public Mono<Void> save(ServerWebExchange serverWebExchange, SecurityContext securityContext) {
+        throw new UnsupportedOperationException("暂不支持");
+    }
+
+    @Override
+    public Mono<SecurityContext> load(ServerWebExchange serverWebExchange) {
+        ServerHttpRequest request = serverWebExchange.getRequest();
+        String authHeader = request.getHeaders().getFirst(HttpHeaders.AUTHORIZATION);
+        if (StringUtils.isNotBlank(authHeader) && StringUtils.startsWith(authHeader, FebsConstant.OAUTH2_TOKEN_TYPE)) {
+            String authToken = StringUtils.substringAfter(authHeader, FebsConstant.OAUTH2_TOKEN_TYPE).trim();
+            Authentication auth = new UsernamePasswordAuthenticationToken(authToken, authToken);
+            return this.authenticationManager.authenticate(auth).map(SecurityContextImpl::new);
+        } else {
+            return Mono.empty();
+        }
+    }
+}

+ 42 - 0
febs-gateway/src/main/java/cc/mrbird/febs/gateway/enhance/auth/WebFluxSecurityCorsConfigure.java

@@ -0,0 +1,42 @@
+package cc.mrbird.febs.gateway.enhance.auth;
+
+import org.springframework.core.Ordered;
+import org.springframework.core.annotation.Order;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpMethod;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.server.reactive.ServerHttpRequest;
+import org.springframework.http.server.reactive.ServerHttpResponse;
+import org.springframework.stereotype.Component;
+import org.springframework.web.cors.reactive.CorsUtils;
+import org.springframework.web.server.ServerWebExchange;
+import org.springframework.web.server.WebFilter;
+import org.springframework.web.server.WebFilterChain;
+import reactor.core.publisher.Mono;
+
+/**
+ * @author MrBird
+ */
+@Component
+@Order(Ordered.HIGHEST_PRECEDENCE)
+public class WebFluxSecurityCorsConfigure implements WebFilter {
+
+    @Override
+    @SuppressWarnings("all")
+    public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
+        ServerHttpRequest request = exchange.getRequest();
+        if (CorsUtils.isCorsRequest(request)) {
+            ServerHttpResponse response = exchange.getResponse();
+            HttpHeaders headers = response.getHeaders();
+            headers.add("Access-Control-Allow-Origin", "*");
+            headers.add("Access-Control-Allow-Methods", "*");
+            headers.add("Access-Control-Max-Age", "3600");
+            headers.add("Access-Control-Allow-Headers", "*");
+            if (request.getMethod() == HttpMethod.OPTIONS) {
+                response.setStatusCode(HttpStatus.OK);
+                return Mono.empty();
+            }
+        }
+        return chain.filter(exchange);
+    }
+}

+ 45 - 0
febs-gateway/src/main/java/cc/mrbird/febs/gateway/enhance/configure/FebsRouteEnhanceConfigure.java

@@ -0,0 +1,45 @@
+package cc.mrbird.febs.gateway.enhance.configure;
+
+import cc.mrbird.febs.common.annotation.EnableFebsLettuceRedis;
+import cc.mrbird.febs.common.entity.constant.FebsConstant;
+import cc.mrbird.febs.gateway.enhance.runner.FebsRouteEnhanceRunner;
+import org.springframework.boot.ApplicationRunner;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.data.mongodb.repository.config.EnableReactiveMongoRepositories;
+import org.springframework.scheduling.annotation.EnableAsync;
+import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
+
+import java.util.concurrent.ThreadPoolExecutor;
+
+/**
+ * @author MrBird
+ */
+@EnableAsync
+@Configuration
+@EnableFebsLettuceRedis
+@EnableReactiveMongoRepositories(basePackages = "cc.mrbird.febs.gateway.enhance.mapper")
+@ConditionalOnProperty(name = "febs.gateway.enhance", havingValue = "true")
+public class FebsRouteEnhanceConfigure {
+
+    @Bean(FebsConstant.ASYNC_POOL)
+    public ThreadPoolTaskExecutor asyncThreadPoolTaskExecutor() {
+        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
+        executor.setCorePoolSize(5);
+        executor.setMaxPoolSize(20);
+        executor.setQueueCapacity(100);
+        executor.setKeepAliveSeconds(30);
+        executor.setThreadNamePrefix("Febs-Gateway-Async-Thread");
+        executor.setWaitForTasksToCompleteOnShutdown(true);
+        executor.setAwaitTerminationSeconds(60);
+        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
+        executor.initialize();
+        return executor;
+    }
+
+    @Bean
+    public ApplicationRunner febsRoutenEhanceRunner() {
+        return new FebsRouteEnhanceRunner();
+    }
+}

+ 55 - 0
febs-gateway/src/main/java/cc/mrbird/febs/gateway/enhance/controller/BlackListController.java

@@ -0,0 +1,55 @@
+package cc.mrbird.febs.gateway.enhance.controller;
+
+import cc.mrbird.febs.common.entity.QueryRequest;
+import cc.mrbird.febs.gateway.enhance.entity.BlackList;
+import cc.mrbird.febs.gateway.enhance.service.BlackListService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.web.bind.annotation.*;
+import reactor.core.publisher.Flux;
+import reactor.core.publisher.Mono;
+
+/**
+ * @author MrBird
+ */
+@RestController
+@RequestMapping("route/auth/blackList")
+public class BlackListController {
+
+    @Autowired
+    private BlackListService blackListService;
+
+    @GetMapping("data")
+    public Flux<BlackList> findUserPages(QueryRequest request, BlackList blackList) {
+        return blackListService.findPages(request, blackList);
+    }
+
+    @GetMapping("count")
+    public Mono<Long> findUserCount(BlackList blackList) {
+        return blackListService.findCount(blackList);
+    }
+
+    @GetMapping("exist")
+    public Flux<BlackList> findByCondition(String ip, String requestUri, String requestMethod) {
+        return blackListService.findByCondition(ip, requestUri, requestMethod);
+    }
+
+    @PostMapping
+    @PreAuthorize("hasAuthority('admin')")
+    public Mono<BlackList> createBlackList(BlackList blackList) {
+        return blackListService.create(blackList);
+    }
+
+    @PutMapping
+    @PreAuthorize("hasAuthority('admin')")
+    public Mono<BlackList> updateBlackList(BlackList blackList) {
+        return blackListService.update(blackList);
+    }
+
+    @DeleteMapping
+    @PreAuthorize("hasAuthority('admin')")
+    public Flux<BlackList> deleteBlackList(String ids) {
+        return blackListService.delete(ids);
+    }
+
+}

+ 40 - 0
febs-gateway/src/main/java/cc/mrbird/febs/gateway/enhance/controller/BlockLogController.java

@@ -0,0 +1,40 @@
+package cc.mrbird.febs.gateway.enhance.controller;
+
+import cc.mrbird.febs.common.entity.QueryRequest;
+import cc.mrbird.febs.gateway.enhance.entity.BlockLog;
+import cc.mrbird.febs.gateway.enhance.service.BlockLogService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+import reactor.core.publisher.Flux;
+import reactor.core.publisher.Mono;
+
+/**
+ * @author MrBird
+ */
+@RestController
+@RequestMapping("route/auth/blockLog")
+public class BlockLogController {
+
+    @Autowired
+    private BlockLogService blockLogService;
+
+    @GetMapping("data")
+    public Flux<BlockLog> findUserPages(QueryRequest request, BlockLog blockLog) {
+        return blockLogService.findPages(request, blockLog);
+    }
+
+    @GetMapping("count")
+    public Mono<Long> findUserCount(BlockLog blockLog) {
+        return blockLogService.findCount(blockLog);
+    }
+
+    @DeleteMapping
+    @PreAuthorize("hasAuthority('admin')")
+    public Flux<BlockLog> deleteBlockLog(String ids){
+        return blockLogService.delete(ids);
+    }
+}

+ 40 - 0
febs-gateway/src/main/java/cc/mrbird/febs/gateway/enhance/controller/RateLimitLogController.java

@@ -0,0 +1,40 @@
+package cc.mrbird.febs.gateway.enhance.controller;
+
+import cc.mrbird.febs.common.entity.QueryRequest;
+import cc.mrbird.febs.gateway.enhance.entity.RateLimitLog;
+import cc.mrbird.febs.gateway.enhance.service.RateLimitLogService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+import reactor.core.publisher.Flux;
+import reactor.core.publisher.Mono;
+
+/**
+ * @author MrBird
+ */
+@RestController
+@RequestMapping("route/auth/rateLimitLog")
+public class RateLimitLogController {
+
+    @Autowired
+    private RateLimitLogService rateLimitLogService;
+
+    @GetMapping("data")
+    public Flux<RateLimitLog> findUserPages(QueryRequest request, RateLimitLog rateLimitLog) {
+        return rateLimitLogService.findPages(request, rateLimitLog);
+    }
+
+    @GetMapping("count")
+    public Mono<Long> findUserCount(RateLimitLog rateLimitLog) {
+        return rateLimitLogService.findCount(rateLimitLog);
+    }
+
+    @DeleteMapping
+    @PreAuthorize("hasAuthority('admin')")
+    public Flux<RateLimitLog> deleteRateLimitLog(String ids){
+        return rateLimitLogService.delete(ids);
+    }
+}

+ 54 - 0
febs-gateway/src/main/java/cc/mrbird/febs/gateway/enhance/controller/RateLimitRuleController.java

@@ -0,0 +1,54 @@
+package cc.mrbird.febs.gateway.enhance.controller;
+
+import cc.mrbird.febs.common.entity.QueryRequest;
+import cc.mrbird.febs.gateway.enhance.entity.RateLimitRule;
+import cc.mrbird.febs.gateway.enhance.service.RateLimitRuleService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.web.bind.annotation.*;
+import reactor.core.publisher.Flux;
+import reactor.core.publisher.Mono;
+
+/**
+ * @author MrBird
+ */
+@RestController
+@RequestMapping("route/auth/rateLimitRule")
+public class RateLimitRuleController {
+
+    @Autowired
+    private RateLimitRuleService rateLimitRuleService;
+
+    @GetMapping("data")
+    public Flux<RateLimitRule> findUserPages(QueryRequest request, RateLimitRule rateLimitRule) {
+        return rateLimitRuleService.findPages(request, rateLimitRule);
+    }
+
+    @GetMapping("count")
+    public Mono<Long> findUserCount(RateLimitRule rateLimitRule) {
+        return rateLimitRuleService.findCount(rateLimitRule);
+    }
+
+    @GetMapping("exist")
+    public Flux<RateLimitRule> findByRequestUriAndRequestMethod(String requestUri, String requestMethod) {
+        return rateLimitRuleService.findByRequestUriAndRequestMethod(requestUri, requestMethod);
+    }
+
+    @PostMapping
+    @PreAuthorize("hasAuthority('admin')")
+    public Mono<RateLimitRule> createRateLimitRule(RateLimitRule rateLimitRule) {
+        return rateLimitRuleService.create(rateLimitRule);
+    }
+
+    @PutMapping
+    @PreAuthorize("hasAuthority('admin')")
+    public Mono<RateLimitRule> updateRateLimitRule(RateLimitRule rateLimitRule) {
+        return rateLimitRuleService.update(rateLimitRule);
+    }
+
+    @DeleteMapping
+    @PreAuthorize("hasAuthority('admin')")
+    public Flux<RateLimitRule> deleteRateLimitRule(String ids) {
+        return rateLimitRuleService.delete(ids);
+    }
+}

+ 40 - 0
febs-gateway/src/main/java/cc/mrbird/febs/gateway/enhance/controller/RouteLogController.java

@@ -0,0 +1,40 @@
+package cc.mrbird.febs.gateway.enhance.controller;
+
+import cc.mrbird.febs.common.entity.QueryRequest;
+import cc.mrbird.febs.gateway.enhance.entity.RouteLog;
+import cc.mrbird.febs.gateway.enhance.service.RouteLogService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+import reactor.core.publisher.Flux;
+import reactor.core.publisher.Mono;
+
+/**
+ * @author MrBird
+ */
+@RestController
+@RequestMapping("route/auth/log")
+public class RouteLogController {
+
+    @Autowired
+    private RouteLogService routeLogService;
+
+    @GetMapping("data")
+    public Flux<RouteLog> findRouteLogsPages(QueryRequest request, RouteLog routeLog) {
+        return routeLogService.findPages(request, routeLog);
+    }
+
+    @GetMapping("count")
+    public Mono<Long> findRouteLogsCount(RouteLog routeLog) {
+        return routeLogService.findCount(routeLog);
+    }
+
+    @DeleteMapping
+    @PreAuthorize("hasAuthority('admin')")
+    public Flux<RouteLog> deleteRouteLogs(String ids) {
+        return routeLogService.delete(ids);
+    }
+}

+ 38 - 0
febs-gateway/src/main/java/cc/mrbird/febs/gateway/enhance/controller/RouteLoginController.java

@@ -0,0 +1,38 @@
+package cc.mrbird.febs.gateway.enhance.controller;
+
+import cc.mrbird.febs.common.entity.FebsResponse;
+import cc.mrbird.febs.gateway.enhance.auth.JWTTokenHelper;
+import cc.mrbird.febs.gateway.enhance.service.RouteUserService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.security.crypto.password.PasswordEncoder;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+import reactor.core.publisher.Mono;
+
+/**
+ * @author MrBird
+ */
+@RestController
+@RequestMapping("route")
+public class RouteLoginController {
+
+    @Autowired
+    private JWTTokenHelper tokenHelper;
+    @Autowired
+    private PasswordEncoder passwordEncoder;
+    @Autowired
+    private RouteUserService routeUserService;
+
+    @GetMapping("login")
+    public Mono<ResponseEntity<FebsResponse>> login(String username, String password) {
+        String error = "认证失败,用户名或密码错误";
+        return routeUserService.findByUsername(username)
+                .map(u -> passwordEncoder.matches(password, u.getPassword()) ?
+                        ResponseEntity.ok(new FebsResponse().data(tokenHelper.generateToken(u))) :
+                        new ResponseEntity<>(new FebsResponse().message(error), HttpStatus.INTERNAL_SERVER_ERROR))
+                .defaultIfEmpty(new ResponseEntity<>(new FebsResponse().message(error), HttpStatus.INTERNAL_SERVER_ERROR));
+    }
+}

+ 55 - 0
febs-gateway/src/main/java/cc/mrbird/febs/gateway/enhance/controller/RouteUserController.java

@@ -0,0 +1,55 @@
+package cc.mrbird.febs.gateway.enhance.controller;
+
+import cc.mrbird.febs.common.entity.QueryRequest;
+import cc.mrbird.febs.gateway.enhance.entity.RouteUser;
+import cc.mrbird.febs.gateway.enhance.service.RouteUserService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.web.bind.annotation.*;
+import reactor.core.publisher.Flux;
+import reactor.core.publisher.Mono;
+
+/**
+ * @author MrBird
+ */
+@RestController
+@RequestMapping("route/auth/user")
+public class RouteUserController {
+
+    @Autowired
+    private RouteUserService routeUserService;
+
+
+    @GetMapping("data")
+    public Flux<RouteUser> findUserPages(QueryRequest request, RouteUser routeUser) {
+        return routeUserService.findPages(request, routeUser);
+    }
+
+    @GetMapping("count")
+    public Mono<Long> findUserCount(RouteUser routeUser) {
+        return routeUserService.findCount(routeUser);
+    }
+
+    @GetMapping("{username}")
+    public Mono<RouteUser> findByUsername(@PathVariable String username) {
+        return routeUserService.findByUsername(username);
+    }
+
+    @PostMapping
+    @PreAuthorize("hasAuthority('admin')")
+    public Mono<RouteUser> createRouteUser(RouteUser routeUser) {
+        return routeUserService.create(routeUser);
+    }
+
+    @PutMapping
+    @PreAuthorize("hasAuthority('admin')")
+    public Mono<RouteUser> updateRouteUser(RouteUser routeUser) {
+        return routeUserService.update(routeUser);
+    }
+
+    @DeleteMapping
+    @PreAuthorize("hasAuthority('admin')")
+    public Flux<RouteUser> deleteRouteUser(String ids) {
+        return routeUserService.delete(ids);
+    }
+}

+ 59 - 0
febs-gateway/src/main/java/cc/mrbird/febs/gateway/enhance/entity/BlackList.java

@@ -0,0 +1,59 @@
+package cc.mrbird.febs.gateway.enhance.entity;
+
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import org.springframework.data.annotation.Id;
+import org.springframework.data.mongodb.core.mapping.Document;
+
+/**
+ * @author MrBird
+ */
+@Data
+@Builder
+@Document
+@NoArgsConstructor
+@AllArgsConstructor
+public class BlackList {
+
+    public static final String CLOSE = "0";
+    public static final String OPEN = "1";
+
+    public static final String METHOD_ALL = "all";
+
+    @Id
+    private String id;
+    /**
+     * 黑名单ip
+     */
+    private String ip;
+    /**
+     * 请求URI
+     */
+    private String requestUri;
+    /**
+     * 请求方法,如果为ALL则表示对所有方法生效
+     */
+    private String requestMethod;
+    /**
+     * 限制时间起
+     */
+    private String limitFrom;
+    /**
+     * 限制时间止
+     */
+    private String limitTo;
+    /**
+     * ip对应地址
+     */
+    private String location;
+    /**
+     * 状态,0关闭,1开启
+     */
+    private String status;
+    /**
+     * 规则创建时间
+     */
+    private String createTime;
+}

+ 48 - 0
febs-gateway/src/main/java/cc/mrbird/febs/gateway/enhance/entity/BlockLog.java

@@ -0,0 +1,48 @@
+package cc.mrbird.febs.gateway.enhance.entity;
+
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import org.springframework.data.annotation.Id;
+import org.springframework.data.annotation.Transient;
+import org.springframework.data.mongodb.core.mapping.Document;
+
+/**
+ * @author MrBird
+ */
+@Data
+@Builder
+@Document
+@NoArgsConstructor
+@AllArgsConstructor
+public class BlockLog {
+
+    @Id
+    private String id;
+    /**
+     * 被拦截请求IP
+     */
+    private String ip;
+    /**
+     * 被拦截请求URI
+     */
+    private String requestUri;
+    /**
+     * 被拦截请求方法
+     */
+    private String requestMethod;
+    /**
+     * IP对应地址
+     */
+    private String location;
+    /**
+     * 拦截时间点
+     */
+    private String createTime;
+
+    @Transient
+    private String createTimeFrom;
+    @Transient
+    private String createTimeTo;
+}

+ 48 - 0
febs-gateway/src/main/java/cc/mrbird/febs/gateway/enhance/entity/RateLimitLog.java

@@ -0,0 +1,48 @@
+package cc.mrbird.febs.gateway.enhance.entity;
+
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import org.springframework.data.annotation.Id;
+import org.springframework.data.annotation.Transient;
+import org.springframework.data.mongodb.core.mapping.Document;
+
+/**
+ * @author MrBird
+ */
+@Data
+@Builder
+@Document
+@NoArgsConstructor
+@AllArgsConstructor
+public class RateLimitLog {
+
+    @Id
+    private String id;
+    /**
+     * 被拦截请求IP
+     */
+    private String ip;
+    /**
+     * 被拦截请求URI
+     */
+    private String requestUri;
+    /**
+     * 被拦截请求方法
+     */
+    private String requestMethod;
+    /**
+     * IP对应地址
+     */
+    private String location;
+    /**
+     * 拦截时间点
+     */
+    private String createTime;
+
+    @Transient
+    private String createTimeFrom;
+    @Transient
+    private String createTimeTo;
+}

+ 65 - 0
febs-gateway/src/main/java/cc/mrbird/febs/gateway/enhance/entity/RateLimitRule.java

@@ -0,0 +1,65 @@
+package cc.mrbird.febs.gateway.enhance.entity;
+
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import org.springframework.data.annotation.Id;
+import org.springframework.data.annotation.Transient;
+import org.springframework.data.mongodb.core.mapping.Document;
+
+/**
+ * @author MrBird
+ */
+@Data
+@Builder
+@Document
+@NoArgsConstructor
+@AllArgsConstructor
+public class RateLimitRule {
+
+    public static final String CLOSE = "0";
+    public static final String OPEN = "1";
+
+    public static final String METHOD_ALL = "all";
+
+    @Id
+    private String id;
+    /**
+     * 请求URI
+     */
+    private String requestUri;
+    /**
+     * 请求方法,如果为ALL则表示对所有方法生效
+     */
+    private String requestMethod;
+    /**
+     * 限制时间起
+     */
+    private String limitFrom;
+    /**
+     * 限制时间止
+     */
+    private String limitTo;
+    /**
+     * 次数
+     */
+    private String count;
+    /**
+     * 时间周期,单位秒
+     */
+    private String intervalSec;
+    /**
+     * 状态,0关闭,1开启
+     */
+    private String status;
+    /**
+     * 规则创建时间
+     */
+    private String createTime;
+
+    @Transient
+    private String createTimeFrom;
+    @Transient
+    private String createTimeTo;
+}

+ 56 - 0
febs-gateway/src/main/java/cc/mrbird/febs/gateway/enhance/entity/RouteLog.java

@@ -0,0 +1,56 @@
+package cc.mrbird.febs.gateway.enhance.entity;
+
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import org.springframework.data.annotation.Id;
+import org.springframework.data.annotation.Transient;
+import org.springframework.data.mongodb.core.mapping.Document;
+
+/**
+ * @author MrBird
+ */
+@Data
+@Builder
+@Document
+@NoArgsConstructor
+@AllArgsConstructor
+public class RouteLog {
+
+    @Id
+    private String id;
+    /**
+     * 请求IP
+     */
+    private String ip;
+    /**
+     * 请求URI
+     */
+    private String requestUri;
+    /**
+     * 目标URI
+     */
+    private String targetUri;
+    /**
+     * 请求方法
+     */
+    private String requestMethod;
+    /**
+     * 目标服务
+     */
+    private String targetServer;
+    /**
+     * 请求时间点
+     */
+    private String createTime;
+    /**
+     * 请求地点
+     */
+    private String location;
+
+    @Transient
+    private String createTimeFrom;
+    @Transient
+    private String createTimeTo;
+}

+ 31 - 0
febs-gateway/src/main/java/cc/mrbird/febs/gateway/enhance/entity/RouteUser.java

@@ -0,0 +1,31 @@
+package cc.mrbird.febs.gateway.enhance.entity;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import org.springframework.data.annotation.Id;
+import org.springframework.data.mongodb.core.mapping.Document;
+
+/**
+ * @author MrBird
+ */
+@Data
+@Builder
+@Document
+@NoArgsConstructor
+@AllArgsConstructor
+public class RouteUser {
+
+    @Id
+    private String id;
+
+    private String username;
+    @JsonIgnore
+    private String password;
+
+    private String roles;
+
+    private String createTime;
+}

+ 17 - 0
febs-gateway/src/main/java/cc/mrbird/febs/gateway/enhance/mapper/BlackListMapper.java

@@ -0,0 +1,17 @@
+package cc.mrbird.febs.gateway.enhance.mapper;
+
+import cc.mrbird.febs.gateway.enhance.entity.BlackList;
+import org.springframework.data.mongodb.repository.ReactiveMongoRepository;
+import org.springframework.stereotype.Repository;
+import reactor.core.publisher.Flux;
+
+@Repository
+public interface BlackListMapper extends ReactiveMongoRepository<BlackList, String> {
+
+    Flux<BlackList> deleteByIdIn(String[] ids);
+
+    Flux<BlackList> findByIpAndRequestUriAndRequestMethod(String ip, String requestUri, String requestMethod);
+
+    Flux<BlackList> findByRequestUriAndRequestMethod(String requestUri, String requestMethod);
+
+}

+ 13 - 0
febs-gateway/src/main/java/cc/mrbird/febs/gateway/enhance/mapper/BlockLogMapper.java

@@ -0,0 +1,13 @@
+package cc.mrbird.febs.gateway.enhance.mapper;
+
+import cc.mrbird.febs.gateway.enhance.entity.BlockLog;
+import org.springframework.data.mongodb.repository.ReactiveMongoRepository;
+import reactor.core.publisher.Flux;
+
+/**
+ * @author MrBird
+ */
+public interface BlockLogMapper extends ReactiveMongoRepository<BlockLog, String> {
+
+    Flux<BlockLog> deleteByIdIn(String[] ids);
+}

+ 12 - 0
febs-gateway/src/main/java/cc/mrbird/febs/gateway/enhance/mapper/RateLimitLogMapper.java

@@ -0,0 +1,12 @@
+package cc.mrbird.febs.gateway.enhance.mapper;
+
+import cc.mrbird.febs.gateway.enhance.entity.RateLimitLog;
+import org.springframework.data.mongodb.repository.ReactiveMongoRepository;
+import org.springframework.stereotype.Repository;
+import reactor.core.publisher.Flux;
+
+@Repository
+public interface RateLimitLogMapper extends ReactiveMongoRepository<RateLimitLog, String> {
+
+    Flux<RateLimitLog> deleteByIdIn(String[] ids);
+}

+ 14 - 0
febs-gateway/src/main/java/cc/mrbird/febs/gateway/enhance/mapper/RateLimitRuleMapper.java

@@ -0,0 +1,14 @@
+package cc.mrbird.febs.gateway.enhance.mapper;
+
+import cc.mrbird.febs.gateway.enhance.entity.RateLimitRule;
+import org.springframework.data.mongodb.repository.ReactiveMongoRepository;
+import org.springframework.stereotype.Repository;
+import reactor.core.publisher.Flux;
+
+@Repository
+public interface RateLimitRuleMapper extends ReactiveMongoRepository<RateLimitRule, String> {
+
+    Flux<RateLimitRule> deleteByIdIn(String[] ids);
+
+    Flux<RateLimitRule> findByRequestUriAndRequestMethod(String requestUri, String requestMethod);
+}

+ 15 - 0
febs-gateway/src/main/java/cc/mrbird/febs/gateway/enhance/mapper/RouteLogMapper.java

@@ -0,0 +1,15 @@
+package cc.mrbird.febs.gateway.enhance.mapper;
+
+import cc.mrbird.febs.gateway.enhance.entity.RouteLog;
+import org.springframework.data.mongodb.repository.ReactiveMongoRepository;
+import org.springframework.stereotype.Repository;
+import reactor.core.publisher.Flux;
+
+/**
+ * @author MrBird
+ */
+@Repository
+public interface RouteLogMapper extends ReactiveMongoRepository<RouteLog, String> {
+
+    Flux<RouteLog> deleteByIdIn(String[] ids);
+}

+ 15 - 0
febs-gateway/src/main/java/cc/mrbird/febs/gateway/enhance/mapper/RouteUserMapper.java

@@ -0,0 +1,15 @@
+package cc.mrbird.febs.gateway.enhance.mapper;
+
+import cc.mrbird.febs.gateway.enhance.entity.RouteUser;
+import org.springframework.data.mongodb.repository.ReactiveMongoRepository;
+import org.springframework.stereotype.Repository;
+import reactor.core.publisher.Flux;
+import reactor.core.publisher.Mono;
+
+@Repository
+public interface RouteUserMapper extends ReactiveMongoRepository<RouteUser, String> {
+
+    Mono<RouteUser> findByUsername(String username);
+
+    Flux<RouteUser> deleteByIdIn(String[] ids);
+}

+ 29 - 0
febs-gateway/src/main/java/cc/mrbird/febs/gateway/enhance/runner/FebsRouteEnhanceRunner.java

@@ -0,0 +1,29 @@
+package cc.mrbird.febs.gateway.enhance.runner;
+
+import cc.mrbird.febs.gateway.enhance.service.BlackListService;
+import cc.mrbird.febs.gateway.enhance.service.RateLimitRuleService;
+import cc.mrbird.febs.gateway.enhance.service.RouteEnhanceCacheService;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.ApplicationArguments;
+import org.springframework.boot.ApplicationRunner;
+
+/**
+ * @author MrBird
+ */
+@Slf4j
+public class FebsRouteEnhanceRunner implements ApplicationRunner {
+
+    @Autowired
+    private RouteEnhanceCacheService cacheService;
+    @Autowired
+    private BlackListService blackListService;
+    @Autowired
+    private RateLimitRuleService rateLimitRuleService;
+
+    @Override
+    public void run(ApplicationArguments args) {
+        cacheService.saveAllBlackList(blackListService.findAll());
+        cacheService.saveAllRateLimitRules(rateLimitRuleService.findAll());
+    }
+}

+ 24 - 0
febs-gateway/src/main/java/cc/mrbird/febs/gateway/enhance/service/BlackListService.java

@@ -0,0 +1,24 @@
+package cc.mrbird.febs.gateway.enhance.service;
+
+import cc.mrbird.febs.common.entity.QueryRequest;
+import cc.mrbird.febs.gateway.enhance.entity.BlackList;
+import reactor.core.publisher.Flux;
+import reactor.core.publisher.Mono;
+
+public interface BlackListService {
+
+    Flux<BlackList> findAll();
+
+    Mono<BlackList> create(BlackList blackList);
+
+    Mono<BlackList> update(BlackList blackList);
+
+    Flux<BlackList> delete(String ids);
+
+    Flux<BlackList> findPages(QueryRequest request, BlackList blackList);
+
+    Mono<Long> findCount(BlackList blackList);
+
+    Flux<BlackList> findByCondition(String ip, String requestUri, String requestMethod);
+
+}

+ 17 - 0
febs-gateway/src/main/java/cc/mrbird/febs/gateway/enhance/service/BlockLogService.java

@@ -0,0 +1,17 @@
+package cc.mrbird.febs.gateway.enhance.service;
+
+import cc.mrbird.febs.common.entity.QueryRequest;
+import cc.mrbird.febs.gateway.enhance.entity.BlockLog;
+import reactor.core.publisher.Flux;
+import reactor.core.publisher.Mono;
+
+public interface BlockLogService {
+
+    Mono<BlockLog> create(BlockLog blockLog);
+
+    Flux<BlockLog> delete(String ids);
+
+    Flux<BlockLog> findPages(QueryRequest request, BlockLog blockLog);
+
+    Mono<Long> findCount(BlockLog blockLog);
+}

+ 19 - 0
febs-gateway/src/main/java/cc/mrbird/febs/gateway/enhance/service/RateLimitLogService.java

@@ -0,0 +1,19 @@
+package cc.mrbird.febs.gateway.enhance.service;
+
+import cc.mrbird.febs.common.entity.QueryRequest;
+import cc.mrbird.febs.gateway.enhance.entity.RateLimitLog;
+import reactor.core.publisher.Flux;
+import reactor.core.publisher.Mono;
+
+/**
+ * @author MrBird
+ */
+public interface RateLimitLogService {
+    Mono<RateLimitLog> create(RateLimitLog rateLimitLog);
+
+    Flux<RateLimitLog> delete(String ids);
+
+    Flux<RateLimitLog> findPages(QueryRequest request, RateLimitLog rateLimitLog);
+
+    Mono<Long> findCount(RateLimitLog rateLimitLog);
+}

+ 23 - 0
febs-gateway/src/main/java/cc/mrbird/febs/gateway/enhance/service/RateLimitRuleService.java

@@ -0,0 +1,23 @@
+package cc.mrbird.febs.gateway.enhance.service;
+
+import cc.mrbird.febs.common.entity.QueryRequest;
+import cc.mrbird.febs.gateway.enhance.entity.RateLimitRule;
+import reactor.core.publisher.Flux;
+import reactor.core.publisher.Mono;
+
+public interface RateLimitRuleService {
+
+    Mono<RateLimitRule> create(RateLimitRule rateLimitRule);
+
+    Flux<RateLimitRule> findAll();
+
+    Flux<RateLimitRule> findByRequestUriAndRequestMethod(String requestUri, String requestMethod);
+
+    Flux<RateLimitRule> findPages(QueryRequest request, RateLimitRule rateLimitRule);
+
+    Mono<Long> findCount(RateLimitRule rateLimitRule);
+
+    Mono<RateLimitRule> update(RateLimitRule rateLimitRule);
+
+    Flux<RateLimitRule> delete(String ids);
+}

+ 34 - 0
febs-gateway/src/main/java/cc/mrbird/febs/gateway/enhance/service/RouteEnhanceCacheService.java

@@ -0,0 +1,34 @@
+package cc.mrbird.febs.gateway.enhance.service;
+
+import cc.mrbird.febs.gateway.enhance.entity.BlackList;
+import cc.mrbird.febs.gateway.enhance.entity.RateLimitRule;
+import reactor.core.publisher.Flux;
+
+import java.util.Set;
+
+public interface RouteEnhanceCacheService {
+
+    void saveAllBlackList(Flux<BlackList> blackList);
+
+    void removeBlackList(BlackList blackList);
+
+    void saveAllRateLimitRules(Flux<RateLimitRule> rateLimitRules);
+
+    void saveBlackList(BlackList blackList);
+
+    Set<Object> getBlackList(String ip);
+
+    Set<Object> getBlackList();
+
+    void saveRateLimitRule(RateLimitRule rateLimitRule);
+
+    Object getRateLimitRule(String uri, String method);
+
+    int getCurrentRequestCount(String uri, String ip);
+
+    void removeRateLimitRule(RateLimitRule rateLimitRule);
+
+    void setCurrentRequestCount(String uri, String ip, Long time);
+
+    void incrCurrentRequestCount(String uri, String ip);
+}

+ 25 - 0
febs-gateway/src/main/java/cc/mrbird/febs/gateway/enhance/service/RouteEnhanceService.java

@@ -0,0 +1,25 @@
+package cc.mrbird.febs.gateway.enhance.service;
+
+import cc.mrbird.febs.common.entity.constant.FebsConstant;
+import org.springframework.scheduling.annotation.Async;
+import org.springframework.web.server.ServerWebExchange;
+import reactor.core.publisher.Mono;
+
+/**
+ * @author MrBird
+ */
+public interface RouteEnhanceService {
+
+    Mono<Void> filterBalckList(ServerWebExchange exchange);
+
+    Mono<Void> filterRateLimit(ServerWebExchange exchange);
+
+    @Async(FebsConstant.ASYNC_POOL)
+    void saveRequestLogs(ServerWebExchange exchange);
+
+    @Async(FebsConstant.ASYNC_POOL)
+    void saveBlockLogs(ServerWebExchange exchange);
+
+    @Async(FebsConstant.ASYNC_POOL)
+    void saveRateLimitLogs(ServerWebExchange exchange);
+}

+ 19 - 0
febs-gateway/src/main/java/cc/mrbird/febs/gateway/enhance/service/RouteLogService.java

@@ -0,0 +1,19 @@
+package cc.mrbird.febs.gateway.enhance.service;
+
+import cc.mrbird.febs.common.entity.QueryRequest;
+import cc.mrbird.febs.gateway.enhance.entity.RouteLog;
+import reactor.core.publisher.Flux;
+import reactor.core.publisher.Mono;
+
+public interface RouteLogService {
+
+    Flux<RouteLog> findAll();
+
+    Mono<RouteLog> create(RouteLog routeLog);
+
+    Flux<RouteLog> delete(String ids);
+
+    Flux<RouteLog> findPages(QueryRequest request, RouteLog routeLog);
+
+    Mono<Long> findCount(RouteLog routeLog);
+}

+ 20 - 0
febs-gateway/src/main/java/cc/mrbird/febs/gateway/enhance/service/RouteUserService.java

@@ -0,0 +1,20 @@
+package cc.mrbird.febs.gateway.enhance.service;
+
+import cc.mrbird.febs.common.entity.QueryRequest;
+import cc.mrbird.febs.gateway.enhance.entity.RouteUser;
+import reactor.core.publisher.Flux;
+import reactor.core.publisher.Mono;
+
+public interface RouteUserService {
+    Mono<RouteUser> create(RouteUser user);
+
+    Mono<RouteUser> update(RouteUser routeUser);
+
+    Flux<RouteUser> delete(String ids);
+
+    Mono<RouteUser> findByUsername(String username);
+
+    Flux<RouteUser> findPages(QueryRequest request, RouteUser routeUser);
+
+    Mono<Long> findCount(RouteUser routeUser);
+}

+ 103 - 0
febs-gateway/src/main/java/cc/mrbird/febs/gateway/enhance/service/impl/BlackListServiceImpl.java

@@ -0,0 +1,103 @@
+package cc.mrbird.febs.gateway.enhance.service.impl;
+
+import cc.mrbird.febs.common.entity.QueryRequest;
+import cc.mrbird.febs.common.utils.DateUtil;
+import cc.mrbird.febs.gateway.enhance.entity.BlackList;
+import cc.mrbird.febs.gateway.enhance.mapper.BlackListMapper;
+import cc.mrbird.febs.gateway.enhance.service.BlackListService;
+import cc.mrbird.febs.gateway.enhance.service.RouteEnhanceCacheService;
+import cc.mrbird.febs.gateway.enhance.utils.AddressUtil;
+import cc.mrbird.febs.gateway.enhance.utils.PageableExecutionUtil;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.BeanUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.mongodb.core.ReactiveMongoTemplate;
+import org.springframework.data.mongodb.core.query.Criteria;
+import org.springframework.data.mongodb.core.query.Query;
+import org.springframework.stereotype.Service;
+import reactor.core.publisher.Flux;
+import reactor.core.publisher.Mono;
+
+import java.time.LocalDateTime;
+
+/**
+ * @author MrBird
+ */
+@Service
+public class BlackListServiceImpl implements BlackListService {
+
+    @Autowired(required = false)
+    private BlackListMapper blackListMapper;
+    @Autowired(required = false)
+    private ReactiveMongoTemplate template;
+    @Autowired
+    private RouteEnhanceCacheService routeEnhanceCacheService;
+
+    @Override
+    public Flux<BlackList> findAll() {
+        return blackListMapper.findAll();
+    }
+
+    @Override
+    public Mono<BlackList> create(BlackList blackList) {
+        blackList.setCreateTime(DateUtil.formatFullTime(LocalDateTime.now(), DateUtil.FULL_TIME_SPLIT_PATTERN));
+        blackList.setLocation(AddressUtil.getCityInfo(blackList.getIp()));
+        return blackListMapper.insert(blackList).doOnSuccess(b -> routeEnhanceCacheService.saveBlackList(blackList));
+    }
+
+    @Override
+    public Mono<BlackList> update(BlackList blackList) {
+        return this.blackListMapper.findById(blackList.getId())
+                .flatMap(b -> {
+                    routeEnhanceCacheService.removeBlackList(b);
+                    BeanUtils.copyProperties(blackList, b);
+                    return this.blackListMapper.save(b);
+                }).doOnSuccess(b -> routeEnhanceCacheService.saveBlackList(b));
+    }
+
+    @Override
+    public Flux<BlackList> delete(String ids) {
+        String[] idArray = StringUtils.splitByWholeSeparatorPreserveAllTokens(ids, ",");
+        return blackListMapper.deleteByIdIn(idArray)
+                .doOnNext(b -> routeEnhanceCacheService.removeBlackList(b));
+    }
+
+    @Override
+    public Flux<BlackList> findPages(QueryRequest request, BlackList blackList) {
+        Query query = getQuery(blackList);
+        return PageableExecutionUtil.getPages(query, request, BlackList.class, template);
+    }
+
+    @Override
+    public Mono<Long> findCount(BlackList blackList) {
+        Query query = getQuery(blackList);
+        return template.count(query, BlackList.class);
+    }
+
+    @Override
+    public Flux<BlackList> findByCondition(String ip, String requestUri, String requestMethod) {
+        if (StringUtils.isBlank(ip)) {
+            return blackListMapper.findByRequestUriAndRequestMethod(requestUri, requestMethod);
+        }
+        return blackListMapper.findByIpAndRequestUriAndRequestMethod(ip, requestUri, requestMethod);
+    }
+
+    private Query getQuery(BlackList blackList) {
+        Query query = new Query();
+        Criteria criteria = new Criteria();
+        if (StringUtils.isNotBlank(blackList.getIp())) {
+            criteria.and("ip").is(blackList.getIp());
+        }
+        if (StringUtils.isNotBlank(blackList.getRequestUri())) {
+            criteria.and("requestUri").is(blackList.getRequestUri());
+        }
+        if (StringUtils.isNotBlank(blackList.getRequestMethod())) {
+            criteria.and("requestMethod").is(blackList.getRequestMethod());
+        }
+        if (StringUtils.isNotBlank(blackList.getStatus())) {
+            criteria.and("status").is(blackList.getStatus());
+        }
+        query.addCriteria(criteria);
+        return query;
+    }
+}

+ 79 - 0
febs-gateway/src/main/java/cc/mrbird/febs/gateway/enhance/service/impl/BlockLogServiceImpl.java

@@ -0,0 +1,79 @@
+package cc.mrbird.febs.gateway.enhance.service.impl;
+
+import cc.mrbird.febs.common.entity.QueryRequest;
+import cc.mrbird.febs.common.utils.DateUtil;
+import cc.mrbird.febs.gateway.enhance.entity.BlockLog;
+import cc.mrbird.febs.gateway.enhance.mapper.BlockLogMapper;
+import cc.mrbird.febs.gateway.enhance.service.BlockLogService;
+import cc.mrbird.febs.gateway.enhance.utils.AddressUtil;
+import cc.mrbird.febs.gateway.enhance.utils.PageableExecutionUtil;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.mongodb.core.ReactiveMongoTemplate;
+import org.springframework.data.mongodb.core.query.Criteria;
+import org.springframework.data.mongodb.core.query.Query;
+import org.springframework.stereotype.Service;
+import reactor.core.publisher.Flux;
+import reactor.core.publisher.Mono;
+
+import java.time.LocalDateTime;
+
+/**
+ * @author MrBird
+ */
+@Service
+public class BlockLogServiceImpl implements BlockLogService {
+
+    @Autowired(required = false)
+    private BlockLogMapper blockLogMapper;
+    @Autowired(required = false)
+    private ReactiveMongoTemplate template;
+
+    @Override
+    public Mono<BlockLog> create(BlockLog blockLog) {
+        blockLog.setCreateTime(DateUtil.formatFullTime(LocalDateTime.now(), DateUtil.FULL_TIME_SPLIT_PATTERN));
+        blockLog.setLocation(AddressUtil.getCityInfo(blockLog.getIp()));
+        return blockLogMapper.insert(blockLog);
+    }
+
+    @Override
+    public Flux<BlockLog> delete(String ids) {
+        String[] idArray = StringUtils.splitByWholeSeparatorPreserveAllTokens(ids, ",");
+        return blockLogMapper.deleteByIdIn(idArray);
+    }
+
+    @Override
+    public Flux<BlockLog> findPages(QueryRequest request, BlockLog blockLog) {
+        Query query = getQuery(blockLog);
+        return PageableExecutionUtil.getPages(query, request, BlockLog.class, template);
+    }
+
+    @Override
+    public Mono<Long> findCount(BlockLog blockLog) {
+        Query query = getQuery(blockLog);
+        return template.count(query, BlockLog.class);
+    }
+
+    private Query getQuery(BlockLog blockLog) {
+        Query query = new Query();
+        Criteria criteria = new Criteria();
+        if (StringUtils.isNotBlank(blockLog.getRequestMethod())) {
+            criteria.and("requestMethod").is(blockLog.getRequestMethod());
+        }
+        if (StringUtils.isNotBlank(blockLog.getIp())) {
+            criteria.and("ip").is(blockLog.getIp());
+        }
+        if (StringUtils.isNotBlank(blockLog.getRequestUri())) {
+            criteria.and("requestUri").is(blockLog.getRequestUri());
+        }
+        if (StringUtils.isNotBlank(blockLog.getCreateTimeFrom())
+                && StringUtils.isNotBlank(blockLog.getCreateTimeTo())) {
+            criteria.andOperator(
+                    Criteria.where("createTime").gt(blockLog.getCreateTimeFrom()),
+                    Criteria.where("createTime").lt(blockLog.getCreateTimeTo())
+            );
+        }
+        query.addCriteria(criteria);
+        return query;
+    }
+}

+ 79 - 0
febs-gateway/src/main/java/cc/mrbird/febs/gateway/enhance/service/impl/RateLimitLogServiceImpl.java

@@ -0,0 +1,79 @@
+package cc.mrbird.febs.gateway.enhance.service.impl;
+
+import cc.mrbird.febs.common.entity.QueryRequest;
+import cc.mrbird.febs.common.utils.DateUtil;
+import cc.mrbird.febs.gateway.enhance.entity.RateLimitLog;
+import cc.mrbird.febs.gateway.enhance.mapper.RateLimitLogMapper;
+import cc.mrbird.febs.gateway.enhance.service.RateLimitLogService;
+import cc.mrbird.febs.gateway.enhance.utils.AddressUtil;
+import cc.mrbird.febs.gateway.enhance.utils.PageableExecutionUtil;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.mongodb.core.ReactiveMongoTemplate;
+import org.springframework.data.mongodb.core.query.Criteria;
+import org.springframework.data.mongodb.core.query.Query;
+import org.springframework.stereotype.Service;
+import reactor.core.publisher.Flux;
+import reactor.core.publisher.Mono;
+
+import java.time.LocalDateTime;
+
+/**
+ * @author MrBird
+ */
+@Service
+public class RateLimitLogServiceImpl implements RateLimitLogService {
+
+    @Autowired(required = false)
+    private RateLimitLogMapper rateLimitLogMapper;
+    @Autowired(required = false)
+    private ReactiveMongoTemplate template;
+
+    @Override
+    public Mono<RateLimitLog> create(RateLimitLog rateLimitLog) {
+        rateLimitLog.setCreateTime(DateUtil.formatFullTime(LocalDateTime.now(), DateUtil.FULL_TIME_SPLIT_PATTERN));
+        rateLimitLog.setLocation(AddressUtil.getCityInfo(rateLimitLog.getIp()));
+        return rateLimitLogMapper.insert(rateLimitLog);
+    }
+
+    @Override
+    public Flux<RateLimitLog> delete(String ids) {
+        String[] idArray = StringUtils.splitByWholeSeparatorPreserveAllTokens(ids, ",");
+        return rateLimitLogMapper.deleteByIdIn(idArray);
+    }
+
+    @Override
+    public Flux<RateLimitLog> findPages(QueryRequest request, RateLimitLog rateLimitLog) {
+        Query query = getQuery(rateLimitLog);
+        return PageableExecutionUtil.getPages(query, request, RateLimitLog.class, template);
+    }
+
+    @Override
+    public Mono<Long> findCount(RateLimitLog rateLimitLog) {
+        Query query = getQuery(rateLimitLog);
+        return template.count(query, RateLimitLog.class);
+    }
+
+    private Query getQuery(RateLimitLog rateLimitLog) {
+        Query query = new Query();
+        Criteria criteria = new Criteria();
+        if (StringUtils.isNotBlank(rateLimitLog.getIp())) {
+            criteria.and("ip").is(rateLimitLog.getIp());
+        }
+        if (StringUtils.isNotBlank(rateLimitLog.getRequestMethod())) {
+            criteria.and("requestMethod").is(rateLimitLog.getRequestMethod());
+        }
+        if (StringUtils.isNotBlank(rateLimitLog.getRequestUri())) {
+            criteria.and("requestUri").is(rateLimitLog.getRequestUri());
+        }
+        if (StringUtils.isNotBlank(rateLimitLog.getCreateTimeFrom())
+                && StringUtils.isNotBlank(rateLimitLog.getCreateTimeTo())) {
+            criteria.andOperator(
+                    Criteria.where("createTime").gt(rateLimitLog.getCreateTimeFrom()),
+                    Criteria.where("createTime").lt(rateLimitLog.getCreateTimeTo())
+            );
+        }
+        query.addCriteria(criteria);
+        return query;
+    }
+}

+ 101 - 0
febs-gateway/src/main/java/cc/mrbird/febs/gateway/enhance/service/impl/RateLimitRuleServiceImpl.java

@@ -0,0 +1,101 @@
+package cc.mrbird.febs.gateway.enhance.service.impl;
+
+import cc.mrbird.febs.common.entity.QueryRequest;
+import cc.mrbird.febs.common.utils.DateUtil;
+import cc.mrbird.febs.gateway.enhance.entity.RateLimitRule;
+import cc.mrbird.febs.gateway.enhance.mapper.RateLimitRuleMapper;
+import cc.mrbird.febs.gateway.enhance.service.RateLimitRuleService;
+import cc.mrbird.febs.gateway.enhance.service.RouteEnhanceCacheService;
+import cc.mrbird.febs.gateway.enhance.utils.PageableExecutionUtil;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.BeanUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.mongodb.core.ReactiveMongoTemplate;
+import org.springframework.data.mongodb.core.query.Criteria;
+import org.springframework.data.mongodb.core.query.Query;
+import org.springframework.stereotype.Service;
+import reactor.core.publisher.Flux;
+import reactor.core.publisher.Mono;
+
+import java.time.LocalDateTime;
+
+/**
+ * @author MrBird
+ */
+@Service
+public class RateLimitRuleServiceImpl implements RateLimitRuleService {
+
+    @Autowired(required = false)
+    private RateLimitRuleMapper rateLimitRuleMapper;
+    @Autowired(required = false)
+    private ReactiveMongoTemplate template;
+    @Autowired
+    private RouteEnhanceCacheService routeEnhanceCacheService;
+
+
+    @Override
+    public Flux<RateLimitRule> findAll() {
+        return rateLimitRuleMapper.findAll();
+    }
+
+    @Override
+    public Flux<RateLimitRule> findByRequestUriAndRequestMethod(String requestUri, String requestMethod) {
+        return rateLimitRuleMapper.findByRequestUriAndRequestMethod(requestUri, requestMethod);
+    }
+
+    @Override
+    public Flux<RateLimitRule> findPages(QueryRequest request, RateLimitRule rateLimitRule) {
+        Query query = getQuery(rateLimitRule);
+        return PageableExecutionUtil.getPages(query, request, RateLimitRule.class, template);
+    }
+
+    @Override
+    public Mono<Long> findCount(RateLimitRule rateLimitRule) {
+        Query query = getQuery(rateLimitRule);
+        return template.count(query, RateLimitRule.class);
+    }
+
+    @Override
+    public Mono<RateLimitRule> create(RateLimitRule rateLimitRule) {
+        rateLimitRule.setCreateTime(DateUtil.formatFullTime(LocalDateTime.now(), DateUtil.FULL_TIME_SPLIT_PATTERN));
+        return rateLimitRuleMapper.insert(rateLimitRule)
+                .doOnSuccess(r -> {
+                    System.out.println(r);
+                    System.out.println(rateLimitRule);
+                    routeEnhanceCacheService.saveRateLimitRule(r);
+                });
+    }
+
+    @Override
+    public Mono<RateLimitRule> update(RateLimitRule rateLimitRule) {
+        return this.rateLimitRuleMapper.findById(rateLimitRule.getId())
+                .flatMap(r -> {
+                    routeEnhanceCacheService.removeRateLimitRule(r);
+                    BeanUtils.copyProperties(rateLimitRule, r);
+                    return this.rateLimitRuleMapper.save(r);
+                }).doOnSuccess(r -> routeEnhanceCacheService.saveRateLimitRule(r));
+    }
+
+    @Override
+    public Flux<RateLimitRule> delete(String ids) {
+        String[] idArray = StringUtils.splitByWholeSeparatorPreserveAllTokens(ids, ",");
+        return rateLimitRuleMapper.deleteByIdIn(idArray)
+                .doOnNext(r -> routeEnhanceCacheService.removeRateLimitRule(r));
+    }
+
+    private Query getQuery(RateLimitRule rateLimitRule) {
+        Query query = new Query();
+        Criteria criteria = new Criteria();
+        if (StringUtils.isNotBlank(rateLimitRule.getRequestMethod())) {
+            criteria.and("requestMethod").is(rateLimitRule.getRequestMethod());
+        }
+        if (StringUtils.isNotBlank(rateLimitRule.getRequestUri())) {
+            criteria.and("requestUri").is(rateLimitRule.getRequestUri());
+        }
+        if (StringUtils.isNotBlank(rateLimitRule.getStatus())) {
+            criteria.and("status").is(rateLimitRule.getStatus());
+        }
+        query.addCriteria(criteria);
+        return query;
+    }
+}

+ 113 - 0
febs-gateway/src/main/java/cc/mrbird/febs/gateway/enhance/service/impl/RouteEnhanceCacheServiceImpl.java

@@ -0,0 +1,113 @@
+package cc.mrbird.febs.gateway.enhance.service.impl;
+
+import cc.mrbird.febs.common.service.RedisService;
+import cc.mrbird.febs.gateway.enhance.entity.BlackList;
+import cc.mrbird.febs.gateway.enhance.entity.RateLimitRule;
+import cc.mrbird.febs.gateway.enhance.service.RouteEnhanceCacheService;
+import cc.mrbird.febs.gateway.enhance.utils.RouteEnhanceCacheUtil;
+import com.alibaba.fastjson.JSONObject;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import reactor.core.publisher.Flux;
+
+import java.util.Set;
+
+/**
+ * @author MrBird
+ */
+@Slf4j
+@Service
+public class RouteEnhanceCacheServiceImpl implements RouteEnhanceCacheService {
+
+    @Autowired(required = false)
+    private RedisService redisService;
+
+    @Override
+    public void saveAllBlackList(Flux<BlackList> blackList) {
+        blackList.subscribe(b -> {
+            String key = StringUtils.isNotBlank(b.getIp()) ?
+                    RouteEnhanceCacheUtil.getBlackListCacheKey(b.getIp()) :
+                    RouteEnhanceCacheUtil.getBlackListCacheKey();
+            String value = JSONObject.toJSONString(b);
+            redisService.sSet(key, value);
+        });
+        log.info("Cache blacklist into redis >>>");
+    }
+
+    @Override
+    public void saveBlackList(BlackList blackList) {
+        String key = StringUtils.isNotBlank(blackList.getIp()) ?
+                RouteEnhanceCacheUtil.getBlackListCacheKey(blackList.getIp()) :
+                RouteEnhanceCacheUtil.getBlackListCacheKey();
+        redisService.sSet(key, JSONObject.toJSONString(blackList));
+    }
+
+    @Override
+    public Set<Object> getBlackList(String ip) {
+        String key = RouteEnhanceCacheUtil.getBlackListCacheKey(ip);
+        return redisService.sGet(key);
+    }
+
+    @Override
+    public Set<Object> getBlackList() {
+        String key = RouteEnhanceCacheUtil.getBlackListCacheKey();
+        return redisService.sGet(key);
+    }
+
+    @Override
+    public void removeBlackList(BlackList blackList) {
+        String key = StringUtils.isNotBlank(blackList.getIp()) ?
+                RouteEnhanceCacheUtil.getBlackListCacheKey(blackList.getIp()) :
+                RouteEnhanceCacheUtil.getBlackListCacheKey();
+        redisService.setRemove(key, JSONObject.toJSONString(blackList));
+    }
+
+    @Override
+    public void saveAllRateLimitRules(Flux<RateLimitRule> rateLimitRules) {
+        rateLimitRules.subscribe(r -> {
+            String key = RouteEnhanceCacheUtil.getRateLimitCacheKey(r.getRequestUri(), r.getRequestMethod());
+            String value = JSONObject.toJSONString(r);
+            redisService.set(key, value);
+        });
+        log.info("Cache rate limit rules into redis >>>");
+    }
+
+    @Override
+    public void saveRateLimitRule(RateLimitRule rateLimitRule) {
+        String key = RouteEnhanceCacheUtil.getRateLimitCacheKey(rateLimitRule.getRequestUri(), rateLimitRule.getRequestMethod());
+        redisService.set(key, JSONObject.toJSONString(rateLimitRule));
+    }
+
+
+    @Override
+    public Object getRateLimitRule(String uri, String method) {
+        String key = RouteEnhanceCacheUtil.getRateLimitCacheKey(uri, method);
+        return redisService.get(key);
+    }
+
+    @Override
+    public int getCurrentRequestCount(String uri, String ip) {
+        String key = RouteEnhanceCacheUtil.getRateLimitCountKey(uri, ip);
+        return redisService.hasKey(key) ? (int) redisService.get(key) : 0;
+    }
+
+    @Override
+    public void removeRateLimitRule(RateLimitRule rateLimitRule) {
+        String key = RouteEnhanceCacheUtil.getRateLimitCacheKey(rateLimitRule.getRequestUri(), rateLimitRule.getRequestMethod());
+        redisService.del(key);
+    }
+
+    @Override
+    public void setCurrentRequestCount(String uri, String ip, Long time) {
+        String key = RouteEnhanceCacheUtil.getRateLimitCountKey(uri, ip);
+        redisService.set(key, 1, time);
+    }
+
+    @Override
+    public void incrCurrentRequestCount(String uri, String ip) {
+        String key = RouteEnhanceCacheUtil.getRateLimitCountKey(uri, ip);
+        redisService.incr(key, 1L);
+    }
+}

+ 221 - 0
febs-gateway/src/main/java/cc/mrbird/febs/gateway/enhance/service/impl/RouteEnhanceServiceImpl.java

@@ -0,0 +1,221 @@
+package cc.mrbird.febs.gateway.enhance.service.impl;
+
+import cc.mrbird.febs.common.entity.FebsResponse;
+import cc.mrbird.febs.common.utils.DateUtil;
+import cc.mrbird.febs.common.utils.FebsUtil;
+import cc.mrbird.febs.gateway.enhance.entity.*;
+import cc.mrbird.febs.gateway.enhance.service.*;
+import cc.mrbird.febs.gateway.enhance.utils.AddressUtil;
+import com.alibaba.fastjson.JSONObject;
+import com.google.common.base.Stopwatch;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.cloud.gateway.route.Route;
+import org.springframework.cloud.gateway.support.ServerWebExchangeUtils;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.MediaType;
+import org.springframework.http.server.reactive.ServerHttpRequest;
+import org.springframework.http.server.reactive.ServerHttpResponse;
+import org.springframework.stereotype.Service;
+import org.springframework.util.AntPathMatcher;
+import org.springframework.web.server.ServerWebExchange;
+import reactor.core.publisher.Mono;
+
+import java.net.URI;
+import java.time.LocalTime;
+import java.util.LinkedHashSet;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/**
+ * @author MrBird
+ */
+@Slf4j
+@Service
+public class RouteEnhanceServiceImpl implements RouteEnhanceService {
+
+    @Autowired
+    private RouteLogService routeLogService;
+    @Autowired
+    private BlockLogService blockLogService;
+    @Autowired
+    private RateLimitLogService rateLimitLogService;
+    @Autowired
+    private RouteEnhanceCacheService routeEnhanceCacheService;
+
+    private AntPathMatcher pathMatcher = new AntPathMatcher();
+    private static final String METHOD_ALL = "ALL";
+
+    @Override
+    public Mono<Void> filterBalckList(ServerWebExchange exchange) {
+        Stopwatch stopwatch = Stopwatch.createStarted();
+        ServerHttpRequest request = exchange.getRequest();
+        ServerHttpResponse response = exchange.getResponse();
+        try {
+            URI originUri = getGatewayOriginalRequestUrl(exchange);
+            if (originUri != null) {
+                String requestIp = FebsUtil.getServerHttpRequestIpAddress(request);
+                String requestMethod = request.getMethodValue();
+                AtomicBoolean forbid = new AtomicBoolean(false);
+                Set<Object> blackList = routeEnhanceCacheService.getBlackList(requestIp);
+                blackList.addAll(routeEnhanceCacheService.getBlackList());
+
+                doBlackListCheck(forbid, blackList, originUri, requestMethod);
+
+                log.info("Blacklist verification completed - {}", stopwatch.stop());
+                if (forbid.get()) {
+                    return FebsUtil.makeWebFluxResponse(response, MediaType.APPLICATION_JSON_VALUE,
+                            HttpStatus.NOT_ACCEPTABLE, new FebsResponse().message("黑名单限制,禁止访问"));
+                }
+            } else {
+                log.info("Request IP not obtained, no blacklist check - {}", stopwatch.stop());
+            }
+        } catch (Exception e) {
+            log.warn("Blacklist verification failed : {} - {}", e.getMessage(), stopwatch.stop());
+        }
+        return null;
+    }
+
+    @Override
+    public Mono<Void> filterRateLimit(ServerWebExchange exchange) {
+        Stopwatch stopwatch = Stopwatch.createStarted();
+        ServerHttpRequest request = exchange.getRequest();
+        ServerHttpResponse response = exchange.getResponse();
+        try {
+            URI originUri = getGatewayOriginalRequestUrl(exchange);
+            if (originUri != null) {
+                String requestIp = FebsUtil.getServerHttpRequestIpAddress(request);
+                String requestMethod = request.getMethodValue();
+                AtomicBoolean limit = new AtomicBoolean(false);
+                Object o = routeEnhanceCacheService.getRateLimitRule(originUri.getPath(), METHOD_ALL);
+                if (o == null) o = routeEnhanceCacheService.getRateLimitRule(originUri.getPath(), requestMethod);
+                if (o != null) {
+                    RateLimitRule rule = JSONObject.parseObject(o.toString(), RateLimitRule.class);
+                    Mono<Void> result = doRateLimitCheck(limit, rule, originUri, requestIp, requestMethod, response);
+                    log.info("Rate limit verification completed - {}", stopwatch.stop());
+                    if (result != null) return result;
+                }
+            } else {
+                log.info("Request IP not obtained, no rate limit filter - {}", stopwatch.stop());
+            }
+        } catch (Exception e) {
+            log.warn("Current limit failure : {} - {}", e.getMessage(), stopwatch.stop());
+        }
+        return null;
+    }
+
+    @Override
+    public void saveRequestLogs(ServerWebExchange exchange) {
+        URI originUri = getGatewayOriginalRequestUrl(exchange);
+        // /auth/user为令牌校验请求,是系统自发行为,非用户请求,故不记录
+        if (!StringUtils.equalsIgnoreCase("/auth/user", originUri.getPath())) {
+            URI url = getGatewayRequestUrl(exchange);
+            Route route = getGatewayRoute(exchange);
+            ServerHttpRequest request = exchange.getRequest();
+            String ipAddress = FebsUtil.getServerHttpRequestIpAddress(request);
+            if (url != null && route != null) {
+                RouteLog routeLog = RouteLog.builder()
+                        .ip(ipAddress)
+                        .requestUri(originUri.getPath())
+                        .targetServer(route.getId())
+                        .targetUri(url.getPath())
+                        .requestMethod(request.getMethodValue())
+                        .location(AddressUtil.getCityInfo(ipAddress))
+                        .build();
+                routeLogService.create(routeLog).subscribe();
+            }
+        }
+    }
+
+    @Override
+    public void saveBlockLogs(ServerWebExchange exchange) {
+        URI originUri = getGatewayOriginalRequestUrl(exchange);
+        ServerHttpRequest request = exchange.getRequest();
+        String requestIp = FebsUtil.getServerHttpRequestIpAddress(request);
+        if (originUri != null) {
+            BlockLog blockLog = BlockLog.builder()
+                    .ip(requestIp)
+                    .requestMethod(request.getMethodValue())
+                    .requestUri(originUri.getPath())
+                    .build();
+            blockLogService.create(blockLog).subscribe();
+            log.info("Store blocked request logs >>>");
+        }
+    }
+
+    @Override
+    public void saveRateLimitLogs(ServerWebExchange exchange) {
+        URI originUri = getGatewayOriginalRequestUrl(exchange);
+        ServerHttpRequest request = exchange.getRequest();
+        String requestIp = FebsUtil.getServerHttpRequestIpAddress(request);
+        if (originUri != null) {
+            RateLimitLog rateLimitLog = RateLimitLog.builder()
+                    .ip(requestIp)
+                    .requestMethod(request.getMethodValue())
+                    .requestUri(originUri.getPath())
+                    .build();
+            rateLimitLogService.create(rateLimitLog).subscribe();
+            log.info("Store rate limit logs >>>");
+        }
+    }
+
+
+    private void doBlackListCheck(AtomicBoolean forbid, Set<Object> blackList, URI uri, String requestMethod) {
+        for (Object o : blackList) {
+            BlackList b = JSONObject.parseObject(o.toString(), BlackList.class);
+            if (pathMatcher.match(b.getRequestUri(), uri.getPath()) && BlackList.OPEN.equals(b.getStatus())) {
+                if (BlackList.METHOD_ALL.equalsIgnoreCase(b.getRequestMethod())
+                        || StringUtils.equalsIgnoreCase(requestMethod, b.getRequestMethod())) {
+                    if (StringUtils.isNotBlank(b.getLimitFrom()) && StringUtils.isNotBlank(b.getLimitTo())) {
+                        if (DateUtil.between(LocalTime.parse(b.getLimitFrom()), LocalTime.parse(b.getLimitTo())))
+                            forbid.set(true);
+                    } else {
+                        forbid.set(true);
+                    }
+                }
+            }
+            if (forbid.get()) break;
+        }
+    }
+
+    private Mono<Void> doRateLimitCheck(AtomicBoolean limit, RateLimitRule rule, URI uri,
+                                        String requestIp, String requestMethod, ServerHttpResponse response) {
+        if (RateLimitRule.OPEN.equals(rule.getStatus())
+                && (BlackList.METHOD_ALL.equalsIgnoreCase(rule.getRequestMethod())
+                || StringUtils.equalsIgnoreCase(requestMethod, rule.getRequestMethod()))) {
+            if (StringUtils.isNotBlank(rule.getLimitFrom()) && StringUtils.isNotBlank(rule.getLimitTo())) {
+                if (DateUtil.between(LocalTime.parse(rule.getLimitFrom()), LocalTime.parse(rule.getLimitTo())))
+                    limit.set(true);
+            } else {
+                limit.set(true);
+            }
+        }
+        if (limit.get()) {
+            String requestUri = uri.getPath();
+            int count = routeEnhanceCacheService.getCurrentRequestCount(requestUri, requestIp);
+            if (count == 0)
+                routeEnhanceCacheService.setCurrentRequestCount(requestUri, requestIp, Long.parseLong(rule.getIntervalSec()));
+            else if (count >= Integer.parseInt(rule.getCount()))
+                return FebsUtil.makeWebFluxResponse(response, MediaType.APPLICATION_JSON_VALUE,
+                        HttpStatus.TOO_MANY_REQUESTS, new FebsResponse().message("访问频率超限,请稍后再试"));
+            else routeEnhanceCacheService.incrCurrentRequestCount(requestUri, requestIp);
+        }
+        return null;
+    }
+
+    private URI getGatewayOriginalRequestUrl(ServerWebExchange exchange) {
+        LinkedHashSet<URI> uris = exchange.getAttribute(ServerWebExchangeUtils.GATEWAY_ORIGINAL_REQUEST_URL_ATTR);
+        URI originUri = null;
+        if (uris != null) originUri = uris.stream().findFirst().orElse(null);
+        return originUri;
+    }
+
+    private URI getGatewayRequestUrl(ServerWebExchange exchange) {
+        return exchange.getAttribute(ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR);
+    }
+
+    private Route getGatewayRoute(ServerWebExchange exchange) {
+        return exchange.getAttribute(ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR);
+    }
+}

+ 84 - 0
febs-gateway/src/main/java/cc/mrbird/febs/gateway/enhance/service/impl/RouteLogServiceImpl.java

@@ -0,0 +1,84 @@
+package cc.mrbird.febs.gateway.enhance.service.impl;
+
+import cc.mrbird.febs.common.entity.QueryRequest;
+import cc.mrbird.febs.common.utils.DateUtil;
+import cc.mrbird.febs.gateway.enhance.entity.RouteLog;
+import cc.mrbird.febs.gateway.enhance.mapper.RouteLogMapper;
+import cc.mrbird.febs.gateway.enhance.service.RouteLogService;
+import cc.mrbird.febs.gateway.enhance.utils.AddressUtil;
+import cc.mrbird.febs.gateway.enhance.utils.PageableExecutionUtil;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.mongodb.core.ReactiveMongoTemplate;
+import org.springframework.data.mongodb.core.query.Criteria;
+import org.springframework.data.mongodb.core.query.Query;
+import org.springframework.stereotype.Service;
+import reactor.core.publisher.Flux;
+import reactor.core.publisher.Mono;
+
+import java.time.LocalDateTime;
+
+/**
+ * @author MrBird
+ */
+@Service
+public class RouteLogServiceImpl implements RouteLogService {
+
+    @Autowired(required = false)
+    private RouteLogMapper routeLogMapper;
+    @Autowired(required = false)
+    private ReactiveMongoTemplate template;
+
+    @Override
+    public Flux<RouteLog> findAll() {
+        return routeLogMapper.findAll();
+    }
+
+    @Override
+    public Mono<RouteLog> create(RouteLog routeLog) {
+        routeLog.setCreateTime(DateUtil.formatFullTime(LocalDateTime.now(), DateUtil.FULL_TIME_SPLIT_PATTERN));
+        routeLog.setLocation(AddressUtil.getCityInfo(routeLog.getIp()));
+        return routeLogMapper.insert(routeLog);
+    }
+
+    @Override
+    public Flux<RouteLog> delete(String ids) {
+        String[] idArray = StringUtils.splitByWholeSeparatorPreserveAllTokens(ids, ",");
+        return routeLogMapper.deleteByIdIn(idArray);
+    }
+
+    @Override
+    public Flux<RouteLog> findPages(QueryRequest request, RouteLog routeLog) {
+        Query query = getQuery(routeLog);
+        return PageableExecutionUtil.getPages(query, request, RouteLog.class, template);
+    }
+
+    @Override
+    public Mono<Long> findCount(RouteLog routeLog) {
+        Query query = getQuery(routeLog);
+        return template.count(query, RouteLog.class);
+    }
+
+    private Query getQuery(RouteLog routeLog) {
+        Query query = new Query();
+        Criteria criteria = new Criteria();
+        if (StringUtils.isNotBlank(routeLog.getIp())) {
+            criteria.and("ip").is(routeLog.getIp());
+        }
+        if (StringUtils.isNotBlank(routeLog.getTargetServer())) {
+            criteria.and("targetServer").is(routeLog.getTargetServer());
+        }
+        if (StringUtils.isNotBlank(routeLog.getRequestMethod())) {
+            criteria.and("requestMethod").is(routeLog.getRequestMethod().toUpperCase());
+        }
+        if (StringUtils.isNotBlank(routeLog.getCreateTimeFrom())
+                && StringUtils.isNotBlank(routeLog.getCreateTimeTo())) {
+            criteria.andOperator(
+                    Criteria.where("createTime").gt(routeLog.getCreateTimeFrom()),
+                    Criteria.where("createTime").lt(routeLog.getCreateTimeTo())
+            );
+        }
+        query.addCriteria(criteria);
+        return query;
+    }
+}

+ 82 - 0
febs-gateway/src/main/java/cc/mrbird/febs/gateway/enhance/service/impl/RouteUserServiceImpl.java

@@ -0,0 +1,82 @@
+package cc.mrbird.febs.gateway.enhance.service.impl;
+
+import cc.mrbird.febs.common.entity.QueryRequest;
+import cc.mrbird.febs.common.utils.DateUtil;
+import cc.mrbird.febs.gateway.enhance.entity.RouteUser;
+import cc.mrbird.febs.gateway.enhance.mapper.RouteUserMapper;
+import cc.mrbird.febs.gateway.enhance.service.RouteUserService;
+import cc.mrbird.febs.gateway.enhance.utils.PageableExecutionUtil;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.mongodb.core.ReactiveMongoTemplate;
+import org.springframework.data.mongodb.core.query.Criteria;
+import org.springframework.data.mongodb.core.query.Query;
+import org.springframework.security.crypto.password.PasswordEncoder;
+import org.springframework.stereotype.Service;
+import reactor.core.publisher.Flux;
+import reactor.core.publisher.Mono;
+
+import java.time.LocalDateTime;
+
+/**
+ * @author MrBird
+ */
+@Service
+public class RouteUserServiceImpl implements RouteUserService {
+
+    @Autowired
+    private PasswordEncoder passwordEncoder;
+    @Autowired(required = false)
+    private RouteUserMapper routeUserMapper;
+    @Autowired(required = false)
+    private ReactiveMongoTemplate template;
+
+    @Override
+    public Mono<RouteUser> create(RouteUser routeUser) {
+        routeUser.setPassword(passwordEncoder.encode(routeUser.getPassword()));
+        routeUser.setCreateTime(DateUtil.formatFullTime(LocalDateTime.now(), DateUtil.FULL_TIME_SPLIT_PATTERN));
+        return routeUserMapper.insert(routeUser);
+    }
+
+    @Override
+    public Mono<RouteUser> update(RouteUser routeUser) {
+        return this.routeUserMapper.findById(routeUser.getId())
+                .flatMap(u -> {
+                    u.setRoles(routeUser.getRoles());
+                    return this.routeUserMapper.save(u);
+                });
+    }
+
+    @Override
+    public Flux<RouteUser> delete(String ids) {
+        String[] idArray = StringUtils.splitByWholeSeparatorPreserveAllTokens(ids, ",");
+        return routeUserMapper.deleteByIdIn(idArray);
+    }
+
+    @Override
+    public Mono<RouteUser> findByUsername(String username) {
+        return routeUserMapper.findByUsername(username);
+    }
+
+    @Override
+    public Flux<RouteUser> findPages(QueryRequest request, RouteUser routeUser) {
+        Query query = getQuery(routeUser);
+        return PageableExecutionUtil.getPages(query, request, RouteUser.class, template);
+    }
+
+    @Override
+    public Mono<Long> findCount(RouteUser routeUser) {
+        Query query = getQuery(routeUser);
+        return template.count(query, RouteUser.class);
+    }
+
+    private Query getQuery(RouteUser routeUser) {
+        Query query = new Query();
+        Criteria criteria = new Criteria();
+        if (StringUtils.isNotBlank(routeUser.getUsername())) {
+            criteria.and("username").is(routeUser.getUsername());
+        }
+        query.addCriteria(criteria);
+        return query;
+    }
+}

+ 58 - 0
febs-gateway/src/main/java/cc/mrbird/febs/gateway/enhance/utils/AddressUtil.java

@@ -0,0 +1,58 @@
+package cc.mrbird.febs.gateway.enhance.utils;
+
+import cc.mrbird.febs.common.entity.constant.FebsConstant;
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.lionsoul.ip2region.DataBlock;
+import org.lionsoul.ip2region.DbConfig;
+import org.lionsoul.ip2region.DbSearcher;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.reflect.Method;
+
+/**
+ * 根据 IP获取地址
+ *
+ * @author MrBird
+ */
+public class AddressUtil {
+
+    private static Logger log = LoggerFactory.getLogger(AddressUtil.class);
+
+    public static String getCityInfo(String ip) {
+        DbSearcher searcher = null;
+        try {
+            String dbPath = AddressUtil.class.getResource("/ip2region/ip2region.db").getPath();
+            File file = new File(dbPath);
+            if (!file.exists()) {
+                String tmpDir = System.getProperties().getProperty(FebsConstant.JAVA_TEMP_DIR);
+                dbPath = tmpDir + "ip.db";
+                file = new File(dbPath);
+                InputStream resourceAsStream = AddressUtil.class.getClassLoader().getResourceAsStream("classpath:ip2region/ip2region.db");
+                if (resourceAsStream != null) {
+                    FileUtils.copyInputStreamToFile(resourceAsStream, file);
+                }
+            }
+            DbConfig config = new DbConfig();
+            searcher = new DbSearcher(config, file.getPath());
+            Method method = searcher.getClass().getMethod("btreeSearch", String.class);
+            DataBlock dataBlock = (DataBlock) method.invoke(searcher, ip);
+            return dataBlock.getRegion();
+        } catch (Exception e) {
+            log.warn("获取地址信息异常,{}", e.getMessage());
+            return StringUtils.EMPTY;
+        } finally {
+            if (searcher != null) {
+                try {
+                    searcher.close();
+                } catch (IOException e) {
+                    e.printStackTrace();
+                }
+            }
+        }
+    }
+}

+ 26 - 0
febs-gateway/src/main/java/cc/mrbird/febs/gateway/enhance/utils/PageableExecutionUtil.java

@@ -0,0 +1,26 @@
+package cc.mrbird.febs.gateway.enhance.utils;
+
+import cc.mrbird.febs.common.entity.QueryRequest;
+import cc.mrbird.febs.common.entity.constant.FebsConstant;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.data.domain.PageRequest;
+import org.springframework.data.domain.Pageable;
+import org.springframework.data.domain.Sort;
+import org.springframework.data.mongodb.core.ReactiveMongoTemplate;
+import org.springframework.data.mongodb.core.query.Query;
+import reactor.core.publisher.Flux;
+
+/**
+ * @author MrBird
+ */
+public class PageableExecutionUtil {
+
+    public static <FEBS> Flux<FEBS> getPages(Query query, QueryRequest request, Class<FEBS> clazz, ReactiveMongoTemplate template) {
+        Sort sort = Sort.by("id").descending();
+        if (StringUtils.isNotBlank(request.getField())
+                && StringUtils.isNotBlank(request.getOrder()))
+            sort = FebsConstant.ORDER_ASC.equals(request.getOrder()) ? Sort.by(request.getField()).ascending() : Sort.by(request.getField()).descending();
+        Pageable pageable = PageRequest.of(request.getPageNum(), request.getPageSize(), sort);
+        return template.find(query.with(pageable), clazz);
+    }
+}

+ 30 - 0
febs-gateway/src/main/java/cc/mrbird/febs/gateway/enhance/utils/RouteEnhanceCacheUtil.java

@@ -0,0 +1,30 @@
+package cc.mrbird.febs.gateway.enhance.utils;
+
+import cc.mrbird.febs.common.entity.constant.FebsConstant;
+
+/**
+ * @author MrBird
+ */
+public class RouteEnhanceCacheUtil {
+
+    private static final String BLACKLIST_CHACHE_KEY_PREFIX = "febs:route:blacklist:";
+    private static final String RATELIMIT_CACHE_KEY_PREFIX = "febs:route:ratelimit:";
+    private static final String RATELIMIT_COUNT_KEY_PREFIX = "febs:route:ratelimit:cout:";
+
+    public static String getBlackListCacheKey(String ip) {
+        if (FebsConstant.LOCALHOST.equalsIgnoreCase(ip)) ip = FebsConstant.LOCALHOST_IP;
+        return String.format("%s%s", BLACKLIST_CHACHE_KEY_PREFIX, ip);
+    }
+
+    public static String getBlackListCacheKey() {
+        return String.format("%sall", BLACKLIST_CHACHE_KEY_PREFIX);
+    }
+
+    public static String getRateLimitCacheKey(String uri, String method) {
+        return String.format("%s%s:%s", RATELIMIT_CACHE_KEY_PREFIX, uri, method);
+    }
+
+    public static String getRateLimitCountKey(String uri, String ip) {
+        return String.format("%s%s:%s", RATELIMIT_COUNT_KEY_PREFIX, uri, ip);
+    }
+}

+ 0 - 105
febs-gateway/src/main/java/cc/mrbird/febs/gateway/filter/FebsGatewayRequestFilter.java

@@ -1,105 +0,0 @@
-package cc.mrbird.febs.gateway.filter;
-
-import cc.mrbird.febs.common.entity.FebsResponse;
-import cc.mrbird.febs.common.entity.constant.FebsConstant;
-import cc.mrbird.febs.gateway.properties.FebsGatewayProperties;
-import com.alibaba.fastjson.JSONObject;
-import lombok.extern.slf4j.Slf4j;
-import org.apache.commons.lang3.ArrayUtils;
-import org.apache.commons.lang3.StringUtils;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.cloud.gateway.filter.GatewayFilterChain;
-import org.springframework.cloud.gateway.filter.GlobalFilter;
-import org.springframework.cloud.gateway.route.Route;
-import org.springframework.core.annotation.Order;
-import org.springframework.core.io.buffer.DataBuffer;
-import org.springframework.http.HttpHeaders;
-import org.springframework.http.HttpStatus;
-import org.springframework.http.MediaType;
-import org.springframework.http.server.reactive.ServerHttpRequest;
-import org.springframework.http.server.reactive.ServerHttpResponse;
-import org.springframework.stereotype.Component;
-import org.springframework.util.AntPathMatcher;
-import org.springframework.util.Base64Utils;
-import org.springframework.web.server.ServerWebExchange;
-import reactor.core.publisher.Mono;
-
-import java.net.URI;
-import java.time.LocalDateTime;
-import java.util.LinkedHashSet;
-
-import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.*;
-
-/**
- * @author MrBird
- */
-@Slf4j
-@Component
-@Order(0)
-public class FebsGatewayRequestFilter implements GlobalFilter {
-
-    @Autowired
-    private FebsGatewayProperties properties;
-    private AntPathMatcher pathMatcher = new AntPathMatcher();
-
-    @Override
-    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
-        ServerHttpRequest request = exchange.getRequest();
-        ServerHttpResponse response = exchange.getResponse();
-
-        Mono<Void> checkForbidUriResult = checkForbidUri(request, response);
-        if (checkForbidUriResult != null) {
-            return checkForbidUriResult;
-        }
-
-        printLog(exchange);
-
-        byte[] token = Base64Utils.encode((FebsConstant.GATEWAY_TOKEN_VALUE).getBytes());
-        String[] headerValues = {new String(token)};
-        ServerHttpRequest build = request.mutate().header(FebsConstant.GATEWAY_TOKEN_HEADER, headerValues).build();
-        ServerWebExchange newExchange = exchange.mutate().request(build).build();
-        return chain.filter(newExchange);
-    }
-
-    private void printLog(ServerWebExchange exchange) {
-        URI url = exchange.getAttribute(GATEWAY_REQUEST_URL_ATTR);
-        Route route = exchange.getAttribute(GATEWAY_ROUTE_ATTR);
-        LinkedHashSet<URI> uris = exchange.getAttribute(GATEWAY_ORIGINAL_REQUEST_URL_ATTR);
-        URI originUri = null;
-        if (uris != null) {
-            originUri = uris.stream().findFirst().orElse(null);
-        }
-        if (url != null && route != null && originUri != null) {
-            log.info("转发请求:{}://{}{} --> 目标服务:{},目标地址:{}://{}{},转发时间:{}",
-                    originUri.getScheme(), originUri.getAuthority(), originUri.getPath(),
-                    route.getId(), url.getScheme(), url.getAuthority(), url.getPath(), LocalDateTime.now()
-            );
-        }
-    }
-
-    private Mono<Void> checkForbidUri(ServerHttpRequest request, ServerHttpResponse response) {
-        String uri = request.getPath().toString();
-        boolean shouldForward = true;
-        String forbidRequestUri = properties.getForbidRequestUri();
-        String[] forbidRequestUris = StringUtils.splitByWholeSeparatorPreserveAllTokens(forbidRequestUri, ",");
-        if (forbidRequestUris != null && ArrayUtils.isNotEmpty(forbidRequestUris)) {
-            for (String u : forbidRequestUris) {
-                if (pathMatcher.match(u, uri)) {
-                    shouldForward = false;
-                }
-            }
-        }
-        if (!shouldForward) {
-            FebsResponse febsResponse = new FebsResponse().message("该URI不允许外部访问");
-            return makeResponse(response, febsResponse);
-        }
-        return null;
-    }
-
-    private Mono<Void> makeResponse(ServerHttpResponse response, FebsResponse febsResponse) {
-        response.setStatusCode(HttpStatus.FORBIDDEN);
-        response.getHeaders().add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);
-        DataBuffer dataBuffer = response.bufferFactory().wrap(JSONObject.toJSONString(febsResponse).getBytes());
-        return response.writeWith(Mono.just(dataBuffer));
-    }
-}

BIN
febs-gateway/src/main/resources/2879260_apicloud.mrbird.cn.pfx


+ 4 - 1
febs-gateway/src/main/resources/bootstrap.yml

@@ -11,6 +11,9 @@ spring:
       discovery:
         server-addr: ${nacos.url}:8001
 
+#  autoconfigure:
+#    exclude: org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration,org.springframework.boot.autoconfigure.mongo.MongoReactiveAutoConfiguration,org.springframework.boot.autoconfigure.data.mongo.MongoReactiveRepositoriesAutoConfiguration,org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration
+
 logging:
   level:
     com:
@@ -18,4 +21,4 @@ logging:
         cloud:
           nacos:
             client:
-              NacosPropertySourceBuilder: error
+              NacosPropertySourceBuilder: error

BIN
febs-gateway/src/main/resources/ip2region/ip2region.db


+ 1 - 1
febs-server/febs-server-system/src/main/java/cc/mrbird/febs/server/system/helper/GeneratorHelper.java

@@ -5,8 +5,8 @@ import cc.mrbird.febs.common.entity.constant.FebsConstant;
 import cc.mrbird.febs.common.entity.constant.GeneratorConstant;
 import cc.mrbird.febs.common.entity.system.Column;
 import cc.mrbird.febs.common.entity.system.GeneratorConfig;
-import cc.mrbird.febs.common.utils.AddressUtil;
 import cc.mrbird.febs.common.utils.FebsUtil;
+import cc.mrbird.febs.server.system.utils.AddressUtil;
 import com.alibaba.fastjson.JSONObject;
 import com.google.common.io.Files;
 import freemarker.template.Configuration;

+ 1 - 1
febs-server/febs-server-system/src/main/java/cc/mrbird/febs/server/system/service/impl/LogServiceImpl.java

@@ -4,10 +4,10 @@ package cc.mrbird.febs.server.system.service.impl;
 import cc.mrbird.febs.common.entity.QueryRequest;
 import cc.mrbird.febs.common.entity.constant.FebsConstant;
 import cc.mrbird.febs.common.entity.system.Log;
-import cc.mrbird.febs.common.utils.AddressUtil;
 import cc.mrbird.febs.common.utils.SortUtil;
 import cc.mrbird.febs.server.system.mapper.LogMapper;
 import cc.mrbird.febs.server.system.service.ILogService;
+import cc.mrbird.febs.server.system.utils.AddressUtil;
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;

+ 1 - 1
febs-server/febs-server-system/src/main/java/cc/mrbird/febs/server/system/service/impl/LoginLogServiceImpl.java

@@ -4,11 +4,11 @@ import cc.mrbird.febs.common.entity.QueryRequest;
 import cc.mrbird.febs.common.entity.constant.FebsConstant;
 import cc.mrbird.febs.common.entity.system.LoginLog;
 import cc.mrbird.febs.common.entity.system.SystemUser;
-import cc.mrbird.febs.common.utils.AddressUtil;
 import cc.mrbird.febs.common.utils.FebsUtil;
 import cc.mrbird.febs.common.utils.SortUtil;
 import cc.mrbird.febs.server.system.mapper.LoginLogMapper;
 import cc.mrbird.febs.server.system.service.ILoginLogService;
+import cc.mrbird.febs.server.system.utils.AddressUtil;
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;

+ 2 - 2
febs-common/src/main/java/cc/mrbird/febs/common/utils/AddressUtil.java → febs-server/febs-server-system/src/main/java/cc/mrbird/febs/server/system/utils/AddressUtil.java

@@ -1,4 +1,4 @@
-package cc.mrbird.febs.common.utils;
+package cc.mrbird.febs.server.system.utils;
 
 import cc.mrbird.febs.common.entity.constant.FebsConstant;
 import org.apache.commons.io.FileUtils;
@@ -43,7 +43,7 @@ public class AddressUtil {
             DataBlock dataBlock = (DataBlock) method.invoke(searcher, ip);
             return dataBlock.getRegion();
         } catch (Exception e) {
-            log.error("获取地址信息异常,{}", e.getMessage());
+            log.warn("获取地址信息异常,{}", e.getMessage());
             return StringUtils.EMPTY;
         } finally {
             if (searcher != null) {