github
This commit is contained in:
@@ -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>
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
package com.demo.gateway.annotations;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.TYPE)
|
||||
public @interface LoggableGlobalFilter {
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -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)) {
|
||||
|
||||
@@ -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"
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user