feat: 初始化
This commit is contained in:
@@ -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"]
|
||||
@@ -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>
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
+45
@@ -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;
|
||||
}
|
||||
}
|
||||
+33
@@ -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));
|
||||
}
|
||||
}
|
||||
+17
@@ -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");
|
||||
}
|
||||
}
|
||||
+56
@@ -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;
|
||||
}
|
||||
}
|
||||
+76
@@ -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();
|
||||
}
|
||||
}
|
||||
+42
@@ -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();
|
||||
}
|
||||
}
|
||||
+43
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
+43
@@ -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);
|
||||
}
|
||||
}
|
||||
+48
@@ -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);
|
||||
}
|
||||
}
|
||||
+55
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
+40
@@ -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);
|
||||
}
|
||||
}
|
||||
+40
@@ -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);
|
||||
}
|
||||
}
|
||||
+54
@@ -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);
|
||||
}
|
||||
}
|
||||
+40
@@ -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);
|
||||
}
|
||||
}
|
||||
+36
@@ -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));
|
||||
}
|
||||
}
|
||||
+54
@@ -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);
|
||||
}
|
||||
+23
@@ -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);
|
||||
}
|
||||
+32
@@ -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);
|
||||
}
|
||||
+26
@@ -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());
|
||||
}
|
||||
}
|
||||
+71
@@ -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);
|
||||
|
||||
}
|
||||
+45
@@ -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);
|
||||
}
|
||||
+45
@@ -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);
|
||||
}
|
||||
+69
@@ -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);
|
||||
}
|
||||
+105
@@ -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);
|
||||
}
|
||||
+52
@@ -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);
|
||||
}
|
||||
+52
@@ -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);
|
||||
}
|
||||
+61
@@ -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);
|
||||
}
|
||||
+117
@@ -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;
|
||||
}
|
||||
}
|
||||
+89
@@ -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;
|
||||
}
|
||||
}
|
||||
+90
@@ -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;
|
||||
}
|
||||
}
|
||||
+107
@@ -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;
|
||||
}
|
||||
}
|
||||
+117
@@ -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);
|
||||
}
|
||||
}
|
||||
+237
@@ -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);
|
||||
}
|
||||
}
|
||||
+94
@@ -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;
|
||||
}
|
||||
}
|
||||
+94
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+29
@@ -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);
|
||||
}
|
||||
}
|
||||
+33
@@ -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
|
||||
Binary file not shown.
@@ -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
|
||||
Reference in New Issue
Block a user