feat: 初始化

This commit is contained in:
2024-04-09 11:34:46 +08:00
commit 39f3acc15f
3209 changed files with 253442 additions and 0 deletions
+7
View File
@@ -0,0 +1,7 @@
FROM openjdk:8u212-jre
MAINTAINER MrBird 852252810@qq.com
COPY ./target/febs-gateway-2.2-RELEASE.jar /febs/febs-gateway-2.2-RELEASE.jar
ADD agent/ /agent
ENTRYPOINT ["java", "-javaagent:/agent/skywalking-agent.jar", "-Dskywalking.agent.service_name=febs-gateway", "-Dskywalking.collector.backend_service=skywalkingIp:11800", "-jar", "/febs/febs-gateway-2.2-RELEASE.jar"]
+101
View File
@@ -0,0 +1,101 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.yida.data</groupId>
<artifactId>febs-cloud</artifactId>
<version>2.2-RELEASE</version>
</parent>
<artifactId>febs-gateway</artifactId>
<name>FEBS-Gateway</name>
<description>FEBS-Gateway服务网关模块</description>
<properties>
<febs-cloud.version>2.2-RELEASE</febs-cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>com.yida.data</groupId>
<artifactId>febs-common-core</artifactId>
<version>${febs-cloud.version}</version>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</exclusion>
<exclusion>
<groupId>com.yida.data</groupId>
<artifactId>febs-common-log-starter</artifactId>
</exclusion>
<exclusion>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
</exclusion>
<exclusion>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-oauth2</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.yida.data</groupId>
<artifactId>febs-common-doc-gateway-starter</artifactId>
<version>${febs-cloud.version}</version>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</exclusion>
</exclusions>
</dependency>
<!--redis-->
<dependency>
<groupId>com.yida.data</groupId>
<artifactId>febs-common-redis-starter</artifactId>
<version>${febs-cloud.version}</version>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!-- 网关增强依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb-reactive</artifactId>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>${jjwt.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<outputDirectory>../febs-jar</outputDirectory>
</configuration>
</plugin>
</plugins>
</build>
</project>
+4
View File
@@ -0,0 +1,4 @@
#!/usr/bin/env bash
mvn package
docker build -t febs-gateway .
@@ -0,0 +1,18 @@
package cc.mrbird.febs.gateway;
import org.springframework.boot.WebApplicationType;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
/**
* @author MrBird
*/
@SpringBootApplication(exclude = {com.yida.data.common.core.handler.BaseExceptionHandler.class})
public class FebsGatewayApplication {
public static void main(String[] args) {
new SpringApplicationBuilder(FebsGatewayApplication.class)
.web(WebApplicationType.REACTIVE)
.run(args);
}
}
@@ -0,0 +1,45 @@
package cc.mrbird.febs.gateway.common.configure;
import cc.mrbird.febs.gateway.common.handler.FebsGatewayExceptionHandler;
import lombok.RequiredArgsConstructor;
import org.springframework.boot.autoconfigure.web.ResourceProperties;
import org.springframework.boot.autoconfigure.web.ServerProperties;
import org.springframework.boot.web.reactive.error.ErrorAttributes;
import org.springframework.boot.web.reactive.error.ErrorWebExceptionHandler;
import org.springframework.context.ApplicationContext;
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 java.util.List;
/**
* @author MrBird
*/
@Configuration
@RequiredArgsConstructor
public class FebsGatewayErrorConfigure {
private final ServerProperties serverProperties;
private final ApplicationContext applicationContext;
private final ResourceProperties resourceProperties;
private final List<ViewResolver> viewResolvers;
private final ServerCodecConfigurer serverCodecConfigurer;
@Bean
@Order(Ordered.HIGHEST_PRECEDENCE)
public ErrorWebExceptionHandler errorWebExceptionHandler(ErrorAttributes errorAttributes) {
FebsGatewayExceptionHandler exceptionHandler = new FebsGatewayExceptionHandler(
errorAttributes,
this.resourceProperties,
this.serverProperties.getError(),
this.applicationContext);
exceptionHandler.setViewResolvers(this.viewResolvers);
exceptionHandler.setMessageWriters(this.serverCodecConfigurer.getWriters());
exceptionHandler.setMessageReaders(this.serverCodecConfigurer.getReaders());
return exceptionHandler;
}
}
@@ -0,0 +1,33 @@
package cc.mrbird.febs.gateway.common.controller;
import com.yida.data.common.core.entity.FebsResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.support.ServerWebExchangeUtils;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.ServerWebExchangeDecorator;
import reactor.core.publisher.Mono;
/**
* @author MrBird
*/
@Slf4j
@RestController
public class FallbackController {
@RequestMapping("fallback/{name}")
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public Mono<FebsResponse> systemFallback(ServerWebExchange exchange, @PathVariable String name) {
// 错误信息
Exception exception = exchange.getAttribute(ServerWebExchangeUtils.HYSTRIX_EXECUTION_EXCEPTION_ATTR);
ServerWebExchange delegate = ((ServerWebExchangeDecorator) exchange).getDelegate();
log.error("调用微服务接口失败: URL={}", delegate.getRequest().getURI(), exception);
String response = "服务访问超时,请稍后再试";
log.error("{},目标微服务:{}", response, name);
return Mono.just(new FebsResponse().message(response));
}
}
@@ -0,0 +1,17 @@
package cc.mrbird.febs.gateway.common.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Mono;
/**
* @author MrBird
*/
@RestController
public class IndexController {
@RequestMapping("/")
public Mono<String> index() {
return Mono.just("febs cloud gateway");
}
}
@@ -0,0 +1,56 @@
package cc.mrbird.febs.gateway.common.filter;
import com.yida.data.common.core.entity.constant.FebsConstant;
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 cc.mrbird.febs.gateway.enhance.service.RouteEnhanceService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import reactor.core.publisher.Mono;
/**
* @author MrBird
*/
@Slf4j
@Component
@Order(0)
@RequiredArgsConstructor
public class FebsGatewayRequestFilter implements GlobalFilter {
private final RouteEnhanceService routeEnhanceService;
private final AntPathMatcher pathMatcher = new AntPathMatcher();
@Value("${febs.gateway.enhance:false}")
private Boolean routeEhance;
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
if (routeEhance) {
Mono<Void> blackListResult = routeEnhanceService.filterBlackList(exchange);
if (blackListResult != null) {
routeEnhanceService.saveBlockLogs(exchange);
return blackListResult;
}
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);
}
}
@@ -0,0 +1,46 @@
package cc.mrbird.febs.gateway.common.filter;
import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR;
import java.net.URI;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.util.UriComponentsBuilder;
import reactor.core.publisher.Mono;
@Component
@Slf4j
public class WebsocketFilter implements GlobalFilter, Ordered {
private final static String DEFAULT_FILTER_PATH = "/out/connect/info";
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
String upgrade = exchange.getRequest().getHeaders().getUpgrade();
URI requestUrl = exchange.getRequiredAttribute(GATEWAY_REQUEST_URL_ATTR);
String scheme = requestUrl.getScheme();
if (!"ws".equals(scheme) && !"wss".equals(scheme)) {
return chain.filter(exchange);
} else if (DEFAULT_FILTER_PATH.equals(requestUrl.getPath())) {
String wsScheme = convertWsToHttp(scheme);
URI wsRequestUrl = UriComponentsBuilder.fromUri(requestUrl).scheme(wsScheme).build().toUri();
exchange.getAttributes().put(GATEWAY_REQUEST_URL_ATTR, wsRequestUrl);
}
return chain.filter(exchange);
}
@Override
public int getOrder() {
// 在websocket过滤器之前处理
return Ordered.LOWEST_PRECEDENCE - 2;
}
static String convertWsToHttp(String scheme) {
scheme = scheme.toLowerCase();
return "ws".equals(scheme) ? "http" : "wss".equals(scheme) ? "https" : scheme;
}
}
@@ -0,0 +1,76 @@
package cc.mrbird.febs.gateway.common.handler;
import org.apache.commons.lang3.StringUtils;
import org.springframework.boot.autoconfigure.web.ErrorProperties;
import org.springframework.boot.autoconfigure.web.ResourceProperties;
import org.springframework.boot.autoconfigure.web.reactive.error.DefaultErrorWebExceptionHandler;
import org.springframework.boot.web.error.ErrorAttributeOptions;
import org.springframework.boot.web.reactive.error.ErrorAttributes;
import org.springframework.cloud.gateway.support.NotFoundException;
import org.springframework.cloud.gateway.support.TimeoutException;
import org.springframework.context.ApplicationContext;
import org.springframework.http.HttpStatus;
import org.springframework.web.reactive.function.server.RequestPredicates;
import org.springframework.web.reactive.function.server.RouterFunction;
import org.springframework.web.reactive.function.server.RouterFunctions;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.reactive.function.server.ServerResponse;
import org.springframework.web.server.ResponseStatusException;
import java.util.HashMap;
import java.util.Map;
import lombok.extern.slf4j.Slf4j;
/**
* @author MrBird
*/
@Slf4j
public class FebsGatewayExceptionHandler extends DefaultErrorWebExceptionHandler {
public FebsGatewayExceptionHandler(ErrorAttributes errorAttributes, ResourceProperties resourceProperties,
ErrorProperties errorProperties, ApplicationContext applicationContext) {
super(errorAttributes, resourceProperties, errorProperties, applicationContext);
}
/**
* 异常处理,定义返回报文格式
*/
@Override
protected Map<String, Object> getErrorAttributes(ServerRequest request, ErrorAttributeOptions options) {
Throwable error = super.getError(request);
log.error(
"请求发生异常,请求URI:{},请求方法:{},异常信息:{}",
request.path(), request.methodName(), error.getMessage()
);
String errorMessage;
if (error instanceof NotFoundException) {
String serverId = StringUtils.substringAfterLast(error.getMessage(), "Unable to find instance for ");
serverId = StringUtils.replace(serverId, "\"", StringUtils.EMPTY);
errorMessage = String.format("无法找到%s服务", serverId);
} else if (StringUtils.containsIgnoreCase(error.getMessage(), "connection refused")) {
errorMessage = "目标服务拒绝连接";
} else if (error instanceof TimeoutException) {
errorMessage = "访问服务超时";
} else if (error instanceof ResponseStatusException
&& StringUtils.containsIgnoreCase(error.getMessage(), HttpStatus.NOT_FOUND.toString())) {
errorMessage = "未找到该资源";
} else {
errorMessage = "网关转发异常";
}
Map<String, Object> errorAttributes = new HashMap<>(3);
errorAttributes.put("message", errorMessage);
return errorAttributes;
}
@Override
@SuppressWarnings("all")
protected RouterFunction<ServerResponse> getRoutingFunction(ErrorAttributes errorAttributes) {
return RouterFunctions.route(RequestPredicates.all(), this::renderErrorResponse);
}
@Override
protected int getHttpStatus(Map<String, Object> errorAttributes) {
return HttpStatus.INTERNAL_SERVER_ERROR.value();
}
}
@@ -0,0 +1,42 @@
package cc.mrbird.febs.gateway.enhance.auth;
import io.jsonwebtoken.Claims;
import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.StringUtils;
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
@RequiredArgsConstructor
public class AuthenticationManager implements ReactiveAuthenticationManager {
private final 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();
}
}
}
@@ -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<>(1);
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);
}
}
@@ -0,0 +1,53 @@
package cc.mrbird.febs.gateway.enhance.auth;
import lombok.RequiredArgsConstructor;
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
@RequiredArgsConstructor
public class SecurityConfigure {
private final AuthenticationManager authenticationManager;
private final 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()
.headers().frameOptions().disable()
.and()
.csrf().disable()
.formLogin().disable()
.httpBasic().disable()
.authenticationManager(authenticationManager)
.securityContextRepository(securityContextRepository)
.authorizeExchange()
.pathMatchers(HttpMethod.OPTIONS).permitAll()
.pathMatchers("/route/auth/**").authenticated()
.anyExchange().permitAll()
.and().build();
}
}
@@ -0,0 +1,43 @@
package cc.mrbird.febs.gateway.enhance.auth;
import com.yida.data.common.core.entity.constant.FebsConstant;
import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.StringUtils;
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
@RequiredArgsConstructor
public class SecurityContextRepository implements ServerSecurityContextRepository {
private final 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();
}
}
}
@@ -0,0 +1,43 @@
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 WebFluxSecurityCorsFilter 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", "*");
headers.add("Access-Control-Allow-Credentials", "true");
if (request.getMethod() == HttpMethod.OPTIONS) {
response.setStatusCode(HttpStatus.OK);
return Mono.empty();
}
}
return chain.filter(exchange);
}
}
@@ -0,0 +1,48 @@
package cc.mrbird.febs.gateway.enhance.configure;
import com.yida.data.common.core.entity.constant.FebsConstant;
import cc.mrbird.febs.gateway.enhance.runner.FebsRouteEnhanceRunner;
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 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
@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 febsRouteEnhanceRunner(RouteEnhanceCacheService cacheService,
BlackListService blackListService,
RateLimitRuleService rateLimitRuleService) {
return new FebsRouteEnhanceRunner(cacheService, blackListService, rateLimitRuleService);
}
}
@@ -0,0 +1,55 @@
package cc.mrbird.febs.gateway.enhance.controller;
import com.yida.data.common.core.entity.QueryRequest;
import cc.mrbird.febs.gateway.enhance.entity.BlackList;
import cc.mrbird.febs.gateway.enhance.service.BlackListService;
import lombok.RequiredArgsConstructor;
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
@RequiredArgsConstructor
@RequestMapping("route/auth/blackList")
public class BlackListController {
private final 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);
}
}
@@ -0,0 +1,40 @@
package cc.mrbird.febs.gateway.enhance.controller;
import com.yida.data.common.core.entity.QueryRequest;
import cc.mrbird.febs.gateway.enhance.entity.BlockLog;
import cc.mrbird.febs.gateway.enhance.service.BlockLogService;
import lombok.RequiredArgsConstructor;
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
@RequiredArgsConstructor
@RequestMapping("route/auth/blockLog")
public class BlockLogController {
private final 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);
}
}
@@ -0,0 +1,40 @@
package cc.mrbird.febs.gateway.enhance.controller;
import com.yida.data.common.core.entity.QueryRequest;
import cc.mrbird.febs.gateway.enhance.entity.RateLimitLog;
import cc.mrbird.febs.gateway.enhance.service.RateLimitLogService;
import lombok.RequiredArgsConstructor;
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
@RequiredArgsConstructor
@RequestMapping("route/auth/rateLimitLog")
public class RateLimitLogController {
private final 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);
}
}
@@ -0,0 +1,54 @@
package cc.mrbird.febs.gateway.enhance.controller;
import com.yida.data.common.core.entity.QueryRequest;
import cc.mrbird.febs.gateway.enhance.entity.RateLimitRule;
import cc.mrbird.febs.gateway.enhance.service.RateLimitRuleService;
import lombok.RequiredArgsConstructor;
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
@RequiredArgsConstructor
@RequestMapping("route/auth/rateLimitRule")
public class RateLimitRuleController {
private final 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);
}
}
@@ -0,0 +1,40 @@
package cc.mrbird.febs.gateway.enhance.controller;
import com.yida.data.common.core.entity.QueryRequest;
import cc.mrbird.febs.gateway.enhance.entity.RouteLog;
import cc.mrbird.febs.gateway.enhance.service.RouteLogService;
import lombok.RequiredArgsConstructor;
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
@RequiredArgsConstructor
@RequestMapping("route/auth/log")
public class RouteLogController {
private final 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);
}
}
@@ -0,0 +1,36 @@
package cc.mrbird.febs.gateway.enhance.controller;
import com.yida.data.common.core.entity.FebsResponse;
import cc.mrbird.febs.gateway.enhance.auth.JwtTokenHelper;
import cc.mrbird.febs.gateway.enhance.service.RouteUserService;
import lombok.RequiredArgsConstructor;
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
@RequiredArgsConstructor
@RequestMapping("route")
public class RouteLoginController {
private final JwtTokenHelper tokenHelper;
private final PasswordEncoder passwordEncoder;
private final 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));
}
}
@@ -0,0 +1,54 @@
package cc.mrbird.febs.gateway.enhance.controller;
import com.yida.data.common.core.entity.QueryRequest;
import cc.mrbird.febs.gateway.enhance.entity.RouteUser;
import cc.mrbird.febs.gateway.enhance.service.RouteUserService;
import lombok.RequiredArgsConstructor;
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
@RequiredArgsConstructor
@RequestMapping("route/auth/user")
public class RouteUserController {
private final 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);
}
}
@@ -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 int CLOSE = 0;
public static final int 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;
}
@@ -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;
}
@@ -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;
}
@@ -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 int CLOSE = 0;
public static final int 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;
}
@@ -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;
}
@@ -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;
}
@@ -0,0 +1,43 @@
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;
import java.util.Collection;
/**
* @author MrBird
*/
@Repository
public interface BlackListMapper extends ReactiveMongoRepository<BlackList, String> {
/**
* 删除黑名单
*
* @param ids 黑名单id
* @return 被删除的黑名单
*/
Flux<BlackList> deleteByIdIn(Collection<String> ids);
/**
* 查询黑名单
*
* @param ip ip
* @param requestUri requestUri
* @param requestMethod requestMethod
* @return 黑名单
*/
Flux<BlackList> findByIpAndRequestUriAndRequestMethod(String ip, String requestUri, String requestMethod);
/**
* 查询黑名单
*
* @param requestUri requestUri
* @param requestMethod requestMethod
* @return 黑名单
*/
Flux<BlackList> findByRequestUriAndRequestMethod(String requestUri, String requestMethod);
}
@@ -0,0 +1,21 @@
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;
import java.util.Collection;
/**
* @author MrBird
*/
public interface BlockLogMapper extends ReactiveMongoRepository<BlockLog, String> {
/**
* 删除拦截日志
*
* @param ids 日志id
* @return 被删除的拦截日志
*/
Flux<BlockLog> deleteByIdIn(Collection<String> ids);
}
@@ -0,0 +1,23 @@
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;
import java.util.Collection;
/**
* @author MrBird
*/
@Repository
public interface RateLimitLogMapper extends ReactiveMongoRepository<RateLimitLog, String> {
/**
* 删除限流日志
*
* @param ids 限流日志id
* @return 被删除的限流日志
*/
Flux<RateLimitLog> deleteByIdIn(Collection<String> ids);
}
@@ -0,0 +1,32 @@
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;
import java.util.Collection;
/**
* @author MrBird
*/
@Repository
public interface RateLimitRuleMapper extends ReactiveMongoRepository<RateLimitRule, String> {
/**
* 删除限流规则
*
* @param ids 限流规则id
* @return 被删除的限流规则
*/
Flux<RateLimitRule> deleteByIdIn(Collection<String> ids);
/**
* 删除限流规则
*
* @param requestUri requestUri
* @param requestMethod requestMethod
* @return 被删除的限流规则
*/
Flux<RateLimitRule> findByRequestUriAndRequestMethod(String requestUri, String requestMethod);
}
@@ -0,0 +1,23 @@
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;
import java.util.Collection;
/**
* @author MrBird
*/
@Repository
public interface RouteLogMapper extends ReactiveMongoRepository<RouteLog, String> {
/**
* 删除路由日志
*
* @param ids 路由日志id
* @return 被删除的路由日志
*/
Flux<RouteLog> deleteByIdIn(Collection<String> ids);
}
@@ -0,0 +1,32 @@
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;
import java.util.Collection;
/**
* @author MrBird
*/
@Repository
public interface RouteUserMapper extends ReactiveMongoRepository<RouteUser, String> {
/**
* 查询路由用户
*
* @param username 用户名
* @return 路由用户
*/
Mono<RouteUser> findByUsername(String username);
/**
* 删除路由用户
*
* @param ids 路由用户id
* @return 被删除的路由用户
*/
Flux<RouteUser> deleteByIdIn(Collection<String> ids);
}
@@ -0,0 +1,26 @@
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.RequiredArgsConstructor;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
/**
* @author MrBird
*/
@RequiredArgsConstructor
public class FebsRouteEnhanceRunner implements ApplicationRunner {
private final RouteEnhanceCacheService cacheService;
private final BlackListService blackListService;
private final RateLimitRuleService rateLimitRuleService;
@Override
public void run(ApplicationArguments args) {
System.out.println("已开启网关增强功能:请求日志、黑名单&限流。");
cacheService.saveAllBlackList(blackListService.findAll());
cacheService.saveAllRateLimitRules(rateLimitRuleService.findAll());
}
}
@@ -0,0 +1,71 @@
package cc.mrbird.febs.gateway.enhance.service;
import com.yida.data.common.core.entity.QueryRequest;
import cc.mrbird.febs.gateway.enhance.entity.BlackList;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
/**
* @author MrBird
*/
public interface BlackListService {
/**
* 查找所有黑名单列表
*
* @return 黑名单列表
*/
Flux<BlackList> findAll();
/**
* 创建黑名单
*
* @param blackList 黑名单
* @return 黑名单
*/
Mono<BlackList> create(BlackList blackList);
/**
* 更新黑名单
*
* @param blackList 黑名单
* @return 黑名单
*/
Mono<BlackList> update(BlackList blackList);
/**
* 删除黑名单
*
* @param ids 黑名单id
* @return 被删除的黑名单
*/
Flux<BlackList> delete(String ids);
/**
* 黑名单分页数据
*
* @param request request
* @param blackList blackList
* @return 黑名单分页数据
*/
Flux<BlackList> findPages(QueryRequest request, BlackList blackList);
/**
* 黑名单分页count
*
* @param blackList blackList
* @return count
*/
Mono<Long> findCount(BlackList blackList);
/**
* 查找黑名单
*
* @param ip ip
* @param requestUri requestUri
* @param requestMethod requestMethod
* @return 黑名单
*/
Flux<BlackList> findByCondition(String ip, String requestUri, String requestMethod);
}
@@ -0,0 +1,45 @@
package cc.mrbird.febs.gateway.enhance.service;
import com.yida.data.common.core.entity.QueryRequest;
import cc.mrbird.febs.gateway.enhance.entity.BlockLog;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
/**
* @author MrBird
*/
public interface BlockLogService {
/**
* 创建拦截日志
*
* @param blockLog 拦截日志
* @return 拦截日志
*/
Mono<BlockLog> create(BlockLog blockLog);
/**
* 删除拦截日志
*
* @param ids 拦截日志id
* @return 被删除的拦截日志
*/
Flux<BlockLog> delete(String ids);
/**
* 查找拦截日志分页数据
*
* @param request request
* @param blockLog blockLog
* @return 拦截日志分页数据
*/
Flux<BlockLog> findPages(QueryRequest request, BlockLog blockLog);
/**
* 查找拦截日志分页数据count
*
* @param blockLog blockLog
* @return count
*/
Mono<Long> findCount(BlockLog blockLog);
}
@@ -0,0 +1,45 @@
package cc.mrbird.febs.gateway.enhance.service;
import com.yida.data.common.core.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 {
/**
* 创建限流日志
*
* @param rateLimitLog 限流日志
* @return 限流日志
*/
Mono<RateLimitLog> create(RateLimitLog rateLimitLog);
/**
* 删除限流日志
*
* @param ids 限流日志id
* @return 被删除的限流日志
*/
Flux<RateLimitLog> delete(String ids);
/**
* 查找限流日志分页数据
*
* @param request request
* @param rateLimitLog rateLimitLog
* @return 限流日志分页数据
*/
Flux<RateLimitLog> findPages(QueryRequest request, RateLimitLog rateLimitLog);
/**
* 查找限流日志分页数据count
*
* @param rateLimitLog rateLimitLog
* @return 限流日志分页数据count
*/
Mono<Long> findCount(RateLimitLog rateLimitLog);
}
@@ -0,0 +1,69 @@
package cc.mrbird.febs.gateway.enhance.service;
import com.yida.data.common.core.entity.QueryRequest;
import cc.mrbird.febs.gateway.enhance.entity.RateLimitRule;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
/**
* @author MrBird
*/
public interface RateLimitRuleService {
/**
* 创建限流规则
*
* @param rateLimitRule 限流规则
* @return 先例规则
*/
Mono<RateLimitRule> create(RateLimitRule rateLimitRule);
/**
* 查找所有限流规则
*
* @return 限流规则
*/
Flux<RateLimitRule> findAll();
/**
* 查找限流规则
*
* @param requestUri requestUri
* @param requestMethod requestMethod
* @return 限流规则
*/
Flux<RateLimitRule> findByRequestUriAndRequestMethod(String requestUri, String requestMethod);
/**
* 获取限流规则分页数据
*
* @param request request
* @param rateLimitRule rateLimitRule
* @return 限流规则分页数据
*/
Flux<RateLimitRule> findPages(QueryRequest request, RateLimitRule rateLimitRule);
/**
* 限流规则分页count
*
* @param rateLimitRule rateLimitRule
* @return count
*/
Mono<Long> findCount(RateLimitRule rateLimitRule);
/**
* 更新限流规则
*
* @param rateLimitRule rateLimitRule
* @return 被更新的限流规则
*/
Mono<RateLimitRule> update(RateLimitRule rateLimitRule);
/**
* 删除限流规则
*
* @param ids 限流规则id
* @return 被删除的限流规则
*/
Flux<RateLimitRule> delete(String ids);
}
@@ -0,0 +1,105 @@
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;
/**
* @author MrBird
*/
public interface RouteEnhanceCacheService {
/**
* 缓存所有黑名单规则
*
* @param blackList 黑名单列表
*/
void saveAllBlackList(Flux<BlackList> blackList);
/**
* 删除黑名单规则缓存
*
* @param blackList 黑名单
*/
void removeBlackList(BlackList blackList);
/**
* 缓存所有限流规则
*
* @param rateLimitRules 限流规则列表
*/
void saveAllRateLimitRules(Flux<RateLimitRule> rateLimitRules);
/**
* 缓存黑名单规则
*
* @param blackList 黑名单规则
*/
void saveBlackList(BlackList blackList);
/**
* 从缓存中获取黑名单规则
*
* @param ip ip
* @return 黑名单
*/
Set<Object> getBlackList(String ip);
/**
* 从缓存中获取黑名单规则
*
* @return 黑名单
*/
Set<Object> getBlackList();
/**
* 缓存限流规则
*
* @param rateLimitRule 限流规则
*/
void saveRateLimitRule(RateLimitRule rateLimitRule);
/**
* 从缓存中获取限流规则
*
* @param uri uri
* @param method method
* @return 限流规则
*/
Object getRateLimitRule(String uri, String method);
/**
* 获取当前请求次数
*
* @param uri uri
* @param ip ip
* @return 次数
*/
int getCurrentRequestCount(String uri, String ip);
/**
* 从缓存中删除限流规则
*
* @param rateLimitRule 限流规则
*/
void removeRateLimitRule(RateLimitRule rateLimitRule);
/**
* 设置请求次数
*
* @param uri uri
* @param ip ip
* @param time time
*/
void setCurrentRequestCount(String uri, String ip, Long time);
/**
* 递增请求次数
*
* @param uri uri
* @param ip ip
*/
void incrCurrentRequestCount(String uri, String ip);
}
@@ -0,0 +1,52 @@
package cc.mrbird.febs.gateway.enhance.service;
import com.yida.data.common.core.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 {
/**
* 根据黑名单规则进行过滤
*
* @param exchange ServerWebExchange
* @return Mono<Void>
*/
Mono<Void> filterBlackList(ServerWebExchange exchange);
/**
* 根据限流规则进行过滤
*
* @param exchange ServerWebExchange
* @return Mono<Void>
*/
Mono<Void> filterRateLimit(ServerWebExchange exchange);
/**
* 异步存储请求日志
*
* @param exchange ServerWebExchange
*/
@Async(FebsConstant.ASYNC_POOL)
void saveRequestLogs(ServerWebExchange exchange);
/**
* 异步存储拦截日志
*
* @param exchange ServerWebExchange
*/
@Async(FebsConstant.ASYNC_POOL)
void saveBlockLogs(ServerWebExchange exchange);
/**
* 异步存储限流日志
*
* @param exchange ServerWebExchange
*/
@Async(FebsConstant.ASYNC_POOL)
void saveRateLimitLogs(ServerWebExchange exchange);
}
@@ -0,0 +1,52 @@
package cc.mrbird.febs.gateway.enhance.service;
import com.yida.data.common.core.entity.QueryRequest;
import cc.mrbird.febs.gateway.enhance.entity.RouteLog;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
/**
* @author MrBird
*/
public interface RouteLogService {
/**
* 查找所有路由日志
*
* @return 路有日志列表
*/
Flux<RouteLog> findAll();
/**
* 创建路由日志
*
* @param routeLog 路由日志
* @return 路由日志
*/
Mono<RouteLog> create(RouteLog routeLog);
/**
* 删除路由日志
*
* @param ids 路由日志id
* @return 被删除的路由日志
*/
Flux<RouteLog> delete(String ids);
/**
* 查找路由日志分页数据
*
* @param request request
* @param routeLog routeLog
* @return 路由日志分页数据
*/
Flux<RouteLog> findPages(QueryRequest request, RouteLog routeLog);
/**
* 查找路由分页数据count
*
* @param routeLog routeLog
* @return count
*/
Mono<Long> findCount(RouteLog routeLog);
}
@@ -0,0 +1,61 @@
package cc.mrbird.febs.gateway.enhance.service;
import com.yida.data.common.core.entity.QueryRequest;
import cc.mrbird.febs.gateway.enhance.entity.RouteUser;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
/**
* @author MrBird
*/
public interface RouteUserService {
/**
* 创建路由用户
*
* @param user 路由用户
* @return 路由用户
*/
Mono<RouteUser> create(RouteUser user);
/**
* 更新路由用户
*
* @param routeUser 路由用户
* @return 路由用户
*/
Mono<RouteUser> update(RouteUser routeUser);
/**
* 删除路由用户
*
* @param ids 路由用户id
* @return 被删除的路由用户
*/
Flux<RouteUser> delete(String ids);
/**
* 根据用户名获取路由用户
*
* @param username 用户名
* @return 路由用户
*/
Mono<RouteUser> findByUsername(String username);
/**
* 查找路由用户分页数据
*
* @param request request
* @param routeUser routeUser
* @return 路由用户分页数据
*/
Flux<RouteUser> findPages(QueryRequest request, RouteUser routeUser);
/**
* 查找路由用户分页数据count
*
* @param routeUser routeUser
* @return count
*/
Mono<Long> findCount(RouteUser routeUser);
}
@@ -0,0 +1,117 @@
package cc.mrbird.febs.gateway.enhance.service.impl;
import com.yida.data.common.core.entity.QueryRequest;
import com.yida.data.common.core.entity.constant.StringConstant;
import com.yida.data.common.core.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 lombok.RequiredArgsConstructor;
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;
import java.util.Arrays;
/**
* @author MrBird
*/
@Service
@RequiredArgsConstructor
public class BlackListServiceImpl implements BlackListService {
private final RouteEnhanceCacheService routeEnhanceCacheService;
private BlackListMapper blackListMapper;
private ReactiveMongoTemplate template;
@Autowired(required = false)
public void setBlackListMapper(BlackListMapper blackListMapper) {
this.blackListMapper = blackListMapper;
}
@Autowired(required = false)
public void setTemplate(ReactiveMongoTemplate template) {
this.template = template;
}
@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));
if (StringUtils.isNotBlank(blackList.getIp())) {
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(routeEnhanceCacheService::saveBlackList);
}
@Override
public Flux<BlackList> delete(String ids) {
String[] idArray = StringUtils.splitByWholeSeparatorPreserveAllTokens(ids, StringConstant.COMMA);
return blackListMapper.deleteByIdIn(Arrays.asList(idArray))
.doOnNext(routeEnhanceCacheService::removeBlackList);
}
@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;
}
}
@@ -0,0 +1,89 @@
package cc.mrbird.febs.gateway.enhance.service.impl;
import com.yida.data.common.core.entity.QueryRequest;
import com.yida.data.common.core.entity.constant.StringConstant;
import com.yida.data.common.core.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;
import java.util.Arrays;
/**
* @author MrBird
*/
@Service
public class BlockLogServiceImpl implements BlockLogService {
private BlockLogMapper blockLogMapper;
private ReactiveMongoTemplate template;
@Autowired(required = false)
public void setBlockLogMapper(BlockLogMapper blockLogMapper) {
this.blockLogMapper = blockLogMapper;
}
@Autowired(required = false)
public void setTemplate(ReactiveMongoTemplate template) {
this.template = 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, StringConstant.COMMA);
return blockLogMapper.deleteByIdIn(Arrays.asList(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.getCreateTimeFrom())
&& StringUtils.isNotBlank(blockLog.getCreateTimeTo())) {
criteria.andOperator(
Criteria.where("createTime").gt(blockLog.getCreateTimeFrom()),
Criteria.where("createTime").lt(blockLog.getCreateTimeTo())
);
}
if (StringUtils.isNotBlank(blockLog.getRequestUri())) {
criteria.and("requestUri").is(blockLog.getRequestUri());
}
query.addCriteria(criteria);
return query;
}
}
@@ -0,0 +1,90 @@
package cc.mrbird.febs.gateway.enhance.service.impl;
import com.yida.data.common.core.entity.QueryRequest;
import com.yida.data.common.core.entity.constant.StringConstant;
import com.yida.data.common.core.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;
import java.util.Arrays;
/**
* @author MrBird
*/
@Service
public class RateLimitLogServiceImpl implements RateLimitLogService {
private RateLimitLogMapper rateLimitLogMapper;
private ReactiveMongoTemplate template;
@Autowired(required = false)
public void setRateLimitLogMapper(RateLimitLogMapper rateLimitLogMapper) {
this.rateLimitLogMapper = rateLimitLogMapper;
}
@Autowired(required = false)
public void setTemplate(ReactiveMongoTemplate template) {
this.template = 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, StringConstant.COMMA);
return rateLimitLogMapper.deleteByIdIn(Arrays.asList(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;
}
}
@@ -0,0 +1,107 @@
package cc.mrbird.febs.gateway.enhance.service.impl;
import com.yida.data.common.core.entity.QueryRequest;
import com.yida.data.common.core.entity.constant.StringConstant;
import com.yida.data.common.core.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 lombok.RequiredArgsConstructor;
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;
import java.util.Arrays;
/**
* @author MrBird
*/
@Service
@RequiredArgsConstructor
public class RateLimitRuleServiceImpl implements RateLimitRuleService {
private final RouteEnhanceCacheService routeEnhanceCacheService;
private RateLimitRuleMapper rateLimitRuleMapper;
private ReactiveMongoTemplate template;
@Autowired(required = false)
public void setRateLimitRuleMapper(RateLimitRuleMapper rateLimitRuleMapper) {
this.rateLimitRuleMapper = rateLimitRuleMapper;
}
@Autowired(required = false)
public void setTemplate(ReactiveMongoTemplate template) {
this.template = template;
}
@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(routeEnhanceCacheService::saveRateLimitRule);
}
@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(routeEnhanceCacheService::saveRateLimitRule);
}
@Override
public Flux<RateLimitRule> delete(String ids) {
String[] idArray = StringUtils.splitByWholeSeparatorPreserveAllTokens(ids, StringConstant.COMMA);
return rateLimitRuleMapper.deleteByIdIn(Arrays.asList(idArray))
.doOnNext(routeEnhanceCacheService::removeRateLimitRule);
}
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;
}
}
@@ -0,0 +1,117 @@
package cc.mrbird.febs.gateway.enhance.service.impl;
import cc.mrbird.febs.common.redis.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 {
private RedisService redisService;
@Autowired(required = false)
public void setRedisService(RedisService redisService) {
this.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);
}
}
@@ -0,0 +1,237 @@
package cc.mrbird.febs.gateway.enhance.service.impl;
import cc.mrbird.febs.gateway.enhance.entity.BlackList;
import cc.mrbird.febs.gateway.enhance.entity.BlockLog;
import cc.mrbird.febs.gateway.enhance.entity.RateLimitLog;
import cc.mrbird.febs.gateway.enhance.entity.RateLimitRule;
import cc.mrbird.febs.gateway.enhance.entity.RouteLog;
import cc.mrbird.febs.gateway.enhance.service.BlockLogService;
import cc.mrbird.febs.gateway.enhance.service.RateLimitLogService;
import cc.mrbird.febs.gateway.enhance.service.RouteEnhanceCacheService;
import cc.mrbird.febs.gateway.enhance.service.RouteEnhanceService;
import cc.mrbird.febs.gateway.enhance.service.RouteLogService;
import cc.mrbird.febs.gateway.enhance.utils.AddressUtil;
import com.alibaba.fastjson.JSONObject;
import com.google.common.base.Stopwatch;
import com.yida.data.common.core.entity.FebsResponse;
import com.yida.data.common.core.utils.DateUtil;
import com.yida.data.common.core.utils.FebsUtil;
import java.net.URI;
import java.time.LocalTime;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
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;
/**
* @author MrBird
*/
@Slf4j
@Service
@RequiredArgsConstructor
public class RouteEnhanceServiceImpl implements RouteEnhanceService {
private static final String METHOD_ALL = "ALL";
private static final String TOKEN_CHECK_URL = "/auth/user";
private final RouteLogService routeLogService;
private final BlockLogService blockLogService;
private final RateLimitLogService rateLimitLogService;
private final RouteEnhanceCacheService routeEnhanceCacheService;
private final AntPathMatcher pathMatcher = new AntPathMatcher();
@Override
public Mono<Void> filterBlackList(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(TOKEN_CHECK_URL, 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 == Integer.parseInt(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) {
boolean isRateLimitRuleHit = RateLimitRule.OPEN == Integer.parseInt(rule.getStatus())
&& (RateLimitRule.METHOD_ALL.equalsIgnoreCase(rule.getRequestMethod())
|| StringUtils.equalsIgnoreCase(requestMethod, rule.getRequestMethod()));
if (isRateLimitRuleHit) {
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);
}
}
@@ -0,0 +1,94 @@
package cc.mrbird.febs.gateway.enhance.service.impl;
import com.yida.data.common.core.entity.QueryRequest;
import com.yida.data.common.core.entity.constant.StringConstant;
import com.yida.data.common.core.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;
import java.util.Arrays;
/**
* @author MrBird
*/
@Service
public class RouteLogServiceImpl implements RouteLogService {
private RouteLogMapper routeLogMapper;
private ReactiveMongoTemplate template;
@Autowired(required = false)
public void setRouteLogMapper(RouteLogMapper routeLogMapper) {
this.routeLogMapper = routeLogMapper;
}
@Autowired(required = false)
public void setTemplate(ReactiveMongoTemplate template) {
this.template = 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, StringConstant.COMMA);
return routeLogMapper.deleteByIdIn(Arrays.asList(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;
}
}
@@ -0,0 +1,94 @@
package cc.mrbird.febs.gateway.enhance.service.impl;
import com.yida.data.common.core.entity.QueryRequest;
import com.yida.data.common.core.entity.constant.StringConstant;
import com.yida.data.common.core.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 lombok.RequiredArgsConstructor;
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;
import java.util.Arrays;
/**
* @author MrBird
*/
@Service
@RequiredArgsConstructor
public class RouteUserServiceImpl implements RouteUserService {
private final PasswordEncoder passwordEncoder;
private RouteUserMapper routeUserMapper;
private ReactiveMongoTemplate template;
@Autowired(required = false)
public void setRouteUserMapper(RouteUserMapper routeUserMapper) {
this.routeUserMapper = routeUserMapper;
}
@Autowired(required = false)
public void setTemplate(ReactiveMongoTemplate template) {
this.template = 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, StringConstant.COMMA);
return routeUserMapper.deleteByIdIn(Arrays.asList(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;
}
}
@@ -0,0 +1,56 @@
package cc.mrbird.febs.gateway.enhance.utils;
import com.yida.data.common.core.entity.constant.FebsConstant;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Method;
import lombok.extern.slf4j.Slf4j;
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;
/**
* 根据 IP获取地址
*
* @author MrBird
*/
@Slf4j
public abstract class AddressUtil {
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) {
log.error("ip2region searcher close error", e);
}
}
}
}
}
@@ -0,0 +1,29 @@
package cc.mrbird.febs.gateway.enhance.utils;
import com.yida.data.common.core.entity.QueryRequest;
import com.yida.data.common.core.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 abstract 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);
}
}
@@ -0,0 +1,33 @@
package cc.mrbird.febs.gateway.enhance.utils;
import com.yida.data.common.core.entity.constant.FebsConstant;
/**
* @author MrBird
*/
public abstract 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,0 +1,8 @@
|------------------------------|
| ____ ____ ___ __ |
| | |_ | |_ | |_) ( (` |
| |_| |_|__ |_|_) _)_) |
| |
| ${spring.application.name} |
| Spring-Boot: ${spring-boot.version} |
|------------------------------|
@@ -0,0 +1,34 @@
spring:
main:
allow-bean-definition-overriding: true
application:
name: FEBS-Gateway
cloud:
nacos:
config:
server-addr: ${nacos.url}
group: DEFAULT_GROUP
prefix: febs-gateway
file-extension: yaml
discovery:
server-addr: ${nacos.url}
boot:
admin:
client:
instance:
prefer-ip: true
logging:
level:
org:
springframework:
boot:
actuate:
endpoint:
EndpointId: error
com:
alibaba:
cloud:
nacos:
client:
NacosPropertySourceBuilder: error
@@ -0,0 +1,55 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="true" scanPeriod="60 seconds" debug="false">
<springProperty scope="context" name="springAppName" source="spring.application.name"/>
<property name="log.path" value="../log/${springAppName}"/>
<property name="log.maxHistory" value="15"/>
<property name="log.colorPattern"
value="%magenta(%d{yyyy-MM-dd HH:mm:ss}) %highlight(%-5level) %boldCyan(${springAppName:-}) %yellow(%thread) %green(%logger) %msg%n"/>
<property name="log.pattern" value="%d{yyyy-MM-dd HH:mm:ss} %-5level ${springAppName:-} %thread %logger %msg%n"/>
<!--输出到控制台-->
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>${log.colorPattern}</pattern>
</encoder>
</appender>
<!--输出到文件-->
<appender name="file_info" class="ch.qos.logback.core.rolling.RollingFileAppender">
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${log.path}/info/info.%d{yyyy-MM-dd}.log</fileNamePattern>
<MaxHistory>${log.maxHistory}</MaxHistory>
</rollingPolicy>
<encoder>
<pattern>${log.pattern}</pattern>
</encoder>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>INFO</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<appender name="file_error" class="ch.qos.logback.core.rolling.RollingFileAppender">
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${log.path}/error/error.%d{yyyy-MM-dd}.log</fileNamePattern>
</rollingPolicy>
<encoder>
<pattern>${log.pattern}</pattern>
</encoder>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>ERROR</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<root level="debug">
<appender-ref ref="console"/>
</root>
<root level="info">
<appender-ref ref="file_info"/>
<appender-ref ref="file_error"/>
</root>
</configuration>
Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

@@ -0,0 +1,10 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>FEBS Cloud Gateway</title>
</head>
<body>
FEBS Cloud网关
</body>
</html>
@@ -0,0 +1,8 @@
# robots.txt
User-agent: Googlebot
Disallow:
User-agent: baiduspider
Disallow:
User-agent: *
Disallow:
Crawl-delay: 60