This commit is contained in:
Administrator
2024-05-16 10:54:20 +08:00
parent 1fcb41bd88
commit 068fdba5a1
11 changed files with 19 additions and 323 deletions

View File

@@ -27,16 +27,6 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>2.20.0</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.17.2</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>

View File

@@ -10,7 +10,6 @@ import org.springframework.boot.autoconfigure.SpringBootApplication;
* @Date: 2021/10/24 23:34
*/
@SpringBootApplication
public class GatewayApplication {
public static void main(String[] args) {

View File

@@ -1,8 +0,0 @@
package com.demo.gateway.annotations;
import java.lang.annotation.*;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface LoggableGlobalFilter {
}

View File

@@ -1,24 +0,0 @@
package com.demo.gateway.config;
import com.demo.gateway.annotations.LoggableGlobalFilter;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
@Aspect
@Component
public class GlobalFilterLoggingAspect {
private Logger logger = LogManager.getLogger(GlobalFilterLoggingAspect.class);
@Before("@within(loggableGlobalFilter) && execution(public * org.springframework.web.server.WebFilter.filter(..)) && args(exchange)")
public void logGlobalFilterExecution(LoggableGlobalFilter loggableGlobalFilter, ServerWebExchange exchange) {
Class<?> filterClass = loggableGlobalFilter.getClass();
logger.info("Executing global filter: " + filterClass.getName() + " for request: " + exchange.getRequest().getURI());
}
}

View File

@@ -21,34 +21,22 @@ import java.util.Collections;
import java.util.List;
import java.util.Map;
public class GatewayExceptionHandler extends AbstractExceptionHandler implements ErrorWebExceptionHandler {
private List<HttpMessageReader<?>> messageReaders = Collections.emptyList();
private List<HttpMessageWriter<?>> messageWriters = Collections.emptyList();
private List<ViewResolver> viewResolvers = Collections.emptyList();
private ThreadLocal<JSONObject> exceptionHandlerResult = new ThreadLocal<>();
public void setMessageReaders(List<HttpMessageReader<?>> messageReaders) {
Assert.notNull(messageReaders, "'messageReaders' must not be null");
this.messageReaders = messageReaders;
}
public void setViewResolvers(List<ViewResolver> viewResolvers) {
this.viewResolvers = viewResolvers;
}
public void setMessageWriters(List<HttpMessageWriter<?>> messageWriters) {
Assert.notNull(messageWriters, "'messageWriters' must not be null");
this.messageWriters = messageWriters;
@@ -56,9 +44,9 @@ public class GatewayExceptionHandler extends AbstractExceptionHandler implements
@Override
public Mono<Void> handle(ServerWebExchange exchange, Throwable ex) {
JSONObject errorInfo = super.buildErrorMap(ex);
System.out.println(JSON.toJSONString(errorInfo));
printErrorInfoToConsole(errorInfo, ex);
if (exchange.getResponse().isCommitted()) {
return Mono.error(ex);
}
@@ -68,9 +56,17 @@ public class GatewayExceptionHandler extends AbstractExceptionHandler implements
.switchIfEmpty(Mono.error(ex))
.flatMap(handler -> handler.handle(newRequest))
.flatMap(response -> write(exchange, response));
}
private void printErrorInfoToConsole(JSONObject errorInfo, Throwable ex) {
System.out.println("Error Occurred:");
System.out.println("---------------");
System.out.println("Message: " + errorInfo.getString("message"));
System.out.println("Cause: " + errorInfo.getString("cause"));
System.out.println("Status: " + errorInfo.getIntValue("status"));
System.out.println("Error: " + errorInfo.getString("error"));
ex.printStackTrace();
}
protected Mono<ServerResponse> renderErrorResponse(ServerRequest request) {
Map<String, Object> result = exceptionHandlerResult.get();
@@ -79,15 +75,12 @@ public class GatewayExceptionHandler extends AbstractExceptionHandler implements
.body(BodyInserters.fromValue(result));
}
private Mono<? extends Void> write(ServerWebExchange exchange,
ServerResponse response) {
private Mono<? extends Void> write(ServerWebExchange exchange, ServerResponse response) {
exchange.getResponse().getHeaders().setContentType(response.headers().getContentType());
return response.writeTo(exchange, new ResponseContext());
}
private class ResponseContext implements ServerResponse.Context {
@Override
public List<HttpMessageWriter<?>> messageWriters() {
return GatewayExceptionHandler.this.messageWriters;
@@ -98,4 +91,4 @@ public class GatewayExceptionHandler extends AbstractExceptionHandler implements
return GatewayExceptionHandler.this.viewResolvers;
}
}
}
}

View File

@@ -1,203 +0,0 @@
package com.demo.gateway.filter;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import org.apache.commons.codec.digest.DigestUtils;
import com.demo.gateway.config.FilterUtils;
import com.demo.gateway.pojo.MyCachedBodyOutputMessage;
import com.demo.gateway.utils.AESUtil;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.cloud.gateway.support.BodyInserterContext;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.HttpHeaders;
import org.springframework.http.ReactiveHttpOutputMessage;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpRequestDecorator;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.web.reactive.function.BodyInserter;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.server.HandlerStrategies;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import java.net.URI;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.concurrent.TimeUnit;
/**
* @Description:
* @Author: rosh
* @Date: 2021/10/26 22:24
*/
//@Configuration
//@Component
public class RequestDecryptionGlobalFilter implements GlobalFilter, Ordered {
public static final String AES_SECURTY = "MTIzNDU2Nzg5MTIzNDU2Nw==";
@Autowired
private RedisTemplate<String, String> redisTemplate;
private static final Logger log = LogManager.getLogger();
private static final String ERROR_MESSAGE = "拒绝服务";
private static final String SIGN_ERROR_MESSAGE = "签名过期";
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
//1 获取时间戳
Long dateTimestamp = getDateTimestamp(exchange.getRequest().getHeaders());
//2 获取RequestId
String requestId = getRequestId(exchange.getRequest().getHeaders());
//3 获取签名
String sign = getSign(exchange.getRequest().getHeaders());
//4 如果是登录不校验Token
// String requestUrl = exchange.getRequest().getPath().value();
// AntPathMatcher pathMatcher = new AntPathMatcher();
// if (!pathMatcher.match("/user/login", requestUrl)) {
// String token = exchange.getRequest().getHeaders().getFirst(UserConstant.TOKEN);
// Claims claim = TokenUtils.getClaim(token);
// if (StringUtils.isBlank(token) || claim == null) {
// return FilterUtils.invalidToken(exchange);
// }
// }
//5 修改请求参数,并获取请求参数
Map<String, Object> paramMap;
try {
paramMap = updateRequestParam(exchange);
} catch (Exception e) {
return FilterUtils.invalidUrl(exchange);
}
//6 获取请求体,修改请求体
ServerRequest serverRequest = ServerRequest.create(exchange, HandlerStrategies.withDefaults().messageReaders());
Mono<String> modifiedBody = serverRequest.bodyToMono(String.class).flatMap(body -> {
String encrypt = AESUtil.decrypt(body, AES_SECURTY);
JSONObject jsonObject = JSON.parseObject(encrypt);
for (Map.Entry<String, Object> entry : jsonObject.entrySet()) {
paramMap.put(entry.getKey(), entry.getValue());
}
checkSign(sign, dateTimestamp, requestId, paramMap);
return Mono.just(encrypt);
});
//创建BodyInserter修改请求体
BodyInserter<Mono<String>, ReactiveHttpOutputMessage> bodyInserter = BodyInserters.fromPublisher(modifiedBody, String.class);
HttpHeaders headers = new HttpHeaders();
headers.putAll(exchange.getRequest().getHeaders());
headers.remove(HttpHeaders.CONTENT_LENGTH);
//创建CachedBodyOutputMessage并且把请求param加入,初始化校验信息
MyCachedBodyOutputMessage outputMessage = new MyCachedBodyOutputMessage(exchange, headers);
outputMessage.initial(paramMap, requestId, sign, dateTimestamp);
return bodyInserter.insert(outputMessage, new BodyInserterContext()).then(Mono.defer(() -> {
ServerHttpRequestDecorator decorator = new ServerHttpRequestDecorator(exchange.getRequest()) {
@Override
public Flux<DataBuffer> getBody() {
Flux<DataBuffer> body = outputMessage.getBody();
if (body.equals(Flux.empty())) {
//验证签名
checkSign(outputMessage.getSign(), outputMessage.getDateTimestamp(), outputMessage.getRequestId(), outputMessage.getParamMap());
}
System.out.println("4. 完成");
return outputMessage.getBody();
}
};
return chain.filter(exchange.mutate().request(decorator).build());
}));
}
public void checkSign(String sign, Long dateTimestamp, String requestId, Map<String, Object> paramMap) {
String str = JSON.toJSONString(paramMap) + requestId + dateTimestamp;
String tempSign = DigestUtils.md5Hex(str);
if (!tempSign.equals(sign)) {
throw new IllegalArgumentException(SIGN_ERROR_MESSAGE);
}
}
/**
* 修改前端传的参数
*/
private Map<String, Object> updateRequestParam(ServerWebExchange exchange) throws NoSuchFieldException, IllegalAccessException {
ServerHttpRequest request = exchange.getRequest();
URI uri = request.getURI();
String query = uri.getQuery();
if (StringUtils.isNotBlank(query) && query.contains("param")) {
return getParamMap(query);
}
return new TreeMap<>();
}
private Map<String, Object> getParamMap(String param) {
Map<String, Object> map = new TreeMap<>();
String[] split = param.split("&");
for (String str : split) {
String[] params = str.split("=");
map.put(params[0], params[1]);
}
return map;
}
private String getSign(HttpHeaders headers) {
List<String> list = headers.get("sign");
if (CollectionUtils.isEmpty(list)) {
throw new IllegalArgumentException(ERROR_MESSAGE);
}
return list.get(0);
}
private Long getDateTimestamp(HttpHeaders httpHeaders) {
List<String> list = httpHeaders.get("timestamp");
if (CollectionUtils.isEmpty(list)) {
throw new IllegalArgumentException(ERROR_MESSAGE);
}
long timestamp = Long.parseLong(list.get(0));
long currentTimeMillis = System.currentTimeMillis();
//有效时长为5分钟
if (currentTimeMillis - timestamp > 1000 * 60 * 5) {
throw new IllegalArgumentException(ERROR_MESSAGE);
}
return timestamp;
}
private String getRequestId(HttpHeaders headers) {
List<String> list = headers.get("requestId");
if (CollectionUtils.isEmpty(list)) {
throw new IllegalArgumentException(ERROR_MESSAGE);
}
String requestId = list.get(0);
//如果requestId存在redis中直接返回
String temp = redisTemplate.opsForValue().get(requestId);
if (StringUtils.isNotBlank(temp)) {
throw new IllegalArgumentException("RequestId 非法");
}
redisTemplate.opsForValue().set(requestId, requestId, 5, TimeUnit.MINUTES);
return requestId;
}
@Override
public int getOrder() {
return 80;
}
}

View File

@@ -1,11 +1,8 @@
package com.demo.gateway.filter;
import com.demo.gateway.annotations.LoggableGlobalFilter;
import com.demo.gateway.utils.AESUtil;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.reactivestreams.Publisher;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
@@ -34,8 +31,8 @@ import java.nio.charset.StandardCharsets;
@Configuration
@Component
public class ResponseEncryptionFilter implements GlobalFilter, Ordered {
private static final String AES_SECURTY = "MTIzNDU2Nzg5MTIzNDU2Nw==";//1234567891234567
private static final Logger log = LogManager.getLogger();
/**
* 加密 过滤器 优先级
* <p>
@@ -48,7 +45,6 @@ public class ResponseEncryptionFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
System.out.println("触发");
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.registerModule(new JavaTimeModule());
@@ -66,7 +62,7 @@ public class ResponseEncryptionFilter implements GlobalFilter, Ordered {
join.read(bytes);
DataBufferUtils.release(join);
System.out.println("加密前 body"+ new String(bytes));
System.out.println("响应加密前:"+ new String(bytes));
byte[] encryption;
@@ -77,10 +73,10 @@ public class ResponseEncryptionFilter implements GlobalFilter, Ordered {
} catch (Exception e) {
encryption = bytes;
log.error("数据类型不是 JSON不加密", e);
System.out.println("数据类型不是 JSON不加密"+e);
}
System.out.println("加密后 body"+ new String(encryption, StandardCharsets.UTF_8));
System.out.println("响应加密后:"+ new String(encryption, StandardCharsets.UTF_8));
HttpHeaders headers = getDelegate().getHeaders();
headers.setContentLength(encryption.length);
return exchange.getResponse().bufferFactory().wrap(encryption);

View File

@@ -1,24 +1,17 @@
package com.demo.gateway.services;
import com.alibaba.fastjson.JSON;
import com.demo.gateway.config.FilterUtils;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.HttpHeaders;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import java.net.URI;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.concurrent.TimeUnit;
@Service
@@ -37,14 +30,13 @@ public class CheckSign {
//3 获取签名
String sign = getSign(request.getHeaders());
//4 获取URL参数
System.out.println("验证签名, 请求参数为"+JSON.toJSONString(paramMap));
checkSign(sign, dateTimestamp, requestId, paramMap);
}
public void checkSign(String sign, Long dateTimestamp, String requestId, Map<String, Object> paramMap) {
String str = JSON.toJSONString(paramMap) + requestId + dateTimestamp;
//打印签名
System.out.println(str);
String tempSign = DigestUtils.md5Hex(str);
if (!tempSign.equals(sign)) {

View File

@@ -2,7 +2,7 @@ server:
port: 8081
spring:
application:
cloud:
name: gateway-service
gateway:
routes:
@@ -18,17 +18,3 @@ spring:
port: 6379
host: redis
#rootLogger:
# level: DEBUG
# appenders:
# - console
#
## 配置控制台输出
#appenders:
# - type: console
# name: CONSOLE
# target: System.out
# layout:
# type: pattern
# pattern: "%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n"

View File

@@ -1,15 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="INFO">
<File name="File" fileName="/tmp/logfile.log">
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
</File>
<Loggers>
<!-- 根日志记录器 -->
<Root level="info">
<AppenderRef ref="Console"/>
</Root>
<Root level="debug">
<AppenderRef ref="Console"/>
</Root>
</Loggers>
</Configuration>

View File

@@ -18,16 +18,6 @@
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>2.14.1</version> <!-- 版本号可以根据你的需求进行更改 -->
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.14.1</version> <!-- 版本号可以根据你的需求进行更改 -->
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>