Browse Source

修改:

1.CpAuthGatewayFilter的逻辑抽取为单独的类,增加针对此filter的异常拦截
dev
wxz 4 years ago
parent
commit
bf0b9dfc63
  1. 214
      epmet-gateway/src/main/java/com/epmet/filter/CpAuthGatewayFilterFactory.java
  2. 263
      epmet-gateway/src/main/java/com/epmet/filter/EpmetGatewayFilter.java
  3. 27
      epmet-gateway/src/main/java/com/epmet/healthcheck/HealthCheckController.java

214
epmet-gateway/src/main/java/com/epmet/filter/CpAuthGatewayFilterFactory.java

@ -1,38 +1,14 @@
package com.epmet.filter; package com.epmet.filter;
import com.alibaba.fastjson.JSON;
import com.epmet.auth.ExternalAuthProcessor; import com.epmet.auth.ExternalAuthProcessor;
import com.epmet.auth.InternalAuthProcessor; import com.epmet.auth.InternalAuthProcessor;
import com.epmet.commons.tools.constant.AppClientConstant; import lombok.extern.slf4j.Slf4j;
import com.epmet.commons.tools.exception.ExceptionUtils;
import com.epmet.commons.tools.exception.RenException;
import com.epmet.commons.tools.utils.IpUtils;
import com.epmet.commons.tools.utils.Result;
import com.epmet.constant.AuthTypeConstant;
import com.epmet.constant.TokenHeaderKeyConstant;
import com.epmet.openapi.constant.AuthTypes;
import com.epmet.openapi.constant.RequestParamKeys;
import com.epmet.utils.ServerHttpRequestUtils;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilter; import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory; import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.springframework.util.MultiValueMap;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import java.nio.charset.StandardCharsets;
import java.util.*; import java.util.*;
/** /**
@ -42,10 +18,9 @@ import java.util.*;
* @since 1.0.0 * @since 1.0.0
*/ */
@Component("CpAuth") @Component("CpAuth")
@Slf4j
public class CpAuthGatewayFilterFactory extends AbstractGatewayFilterFactory<CpAuthGatewayFilterFactory.CpAuthConfig> { public class CpAuthGatewayFilterFactory extends AbstractGatewayFilterFactory<CpAuthGatewayFilterFactory.CpAuthConfig> {
private Logger logger = LoggerFactory.getLogger(getClass());
@Autowired @Autowired
private InternalAuthProcessor internalAuthProcessor; private InternalAuthProcessor internalAuthProcessor;
@ -61,181 +36,6 @@ public class CpAuthGatewayFilterFactory extends AbstractGatewayFilterFactory<CpA
super(CpAuthConfig.class); super(CpAuthConfig.class);
} }
@Override
public GatewayFilter apply(CpAuthConfig config) {
return (exchange, chain) -> {
if (!config.isEnabled()) {
return chain.filter(exchange);
}
ServerHttpRequest request = exchange.getRequest();
//0.添加流水号
String tranSerial = getTranserialFromRequestHeader(request);
if (StringUtils.isBlank(tranSerial)) {
tranSerial = generateTransactionSerial();
// 设置当前线程名
request.mutate().header(AppClientConstant.TRANSACTION_SERIAL_KEY, new String[]{tranSerial});
}
Thread.currentThread().setName(tranSerial);
//1.打印请求信息
logRequest(request);
//2.获取请求路径,参数
String requestUri = request.getPath().pathWithinApplication().value();
MultiValueMap<String, String> queryParams = request.getQueryParams();
String queryParamsStr = convertQueryParams2String(queryParams);
logger.info("当前requestUri=[" + requestUri.concat(queryParamsStr) + "],客户端Id:{}", IpUtils.getClientIp(request));
//3.认证
String authType = getAuthType(request);
logger.info("认证类型为:{}", authType);
try {
switch (authType) {
case AuthTypeConstant.AUTH_TYPE_ALL:
externalAuthProcessor.auth(exchange, chain);
internalAuthProcessor.auth(exchange, chain);
break;
case AuthTypeConstant.AUTH_TYPE_EXTERNAL:
externalAuthProcessor.auth(exchange, chain);
break;
case AuthTypeConstant.AUTH_TYPE_INTERNAL:
internalAuthProcessor.auth(exchange, chain);
break;
}
} catch (RenException e) {
logger.error("CpAuthGatewayFilterFactory认证出错RenException,错误信息:{}", ExceptionUtils.getErrorStackTrace(e));
return response(exchange, new Result<>().error(e.getCode(), e.getMessage()));
} catch (Exception e) {
logger.error("CpAuthGatewayFilterFactory认证出错Exception,错误信息:{}", ExceptionUtils.getErrorStackTrace(e));
return response(exchange, new Result<>().error(e.getMessage()));
}
return chain.filter(exchange);
};
}
/**
* @Description 打印请求头
* @param request 请求
* @return void
* @author wxz
* @date 2021.08.18 11:18:00
*/
private void logRequest(ServerHttpRequest request) {
HttpHeaders headers = request.getHeaders();
StringBuilder sb = new StringBuilder("请求头:");
for (Map.Entry<String, List<String>> entry : headers.entrySet()) {
String headerKey = entry.getKey();
List<String> headerValue = entry.getValue();
sb.append(headerKey).append(":").append(headerValue).append(",");
}
logger.info(sb.toString());
}
/**
* @Description 从request请求头中获取传递过来的流水号
* @param request
* @return java.lang.String
* @author wxz
* @date 2021.08.18 15:55:30
*/
private String getTranserialFromRequestHeader(ServerHttpRequest request) {
List<String> tranSerials = request.getHeaders().get(AppClientConstant.TRANSACTION_SERIAL_KEY);
return CollectionUtils.isEmpty(tranSerials) ? null : tranSerials.get(0);
}
/**
* @return
* @Description 将url参数转化为String
* @author wxz
* @date 2021.08.11 23:12
*/
private String convertQueryParams2String(MultiValueMap<String, String> queryParams) {
try {
if (queryParams == null || queryParams.size() == 0) {
return "";
}
StringBuilder sb = new StringBuilder("");
queryParams.entrySet().forEach(entry -> {
String key = entry.getKey();
List<String> values = entry.getValue();
String value = "";
if (values != null && values.size() > 0) {
value = values.get(0);
}
sb.append("&").append(key).append("=").append(value);
});
String result = sb.toString();
if (result.startsWith("&")) {
result = result.substring(1);
return "?".concat(result);
}
return "";
} catch (Exception e) {
logger.warn("gateway中将url参数转化为String失败,程序继续执行,错误信息:".concat(ExceptionUtils.getErrorStackTrace(e)));
return "";
}
}
/**
* 判断需要执行的认证方式(内部应用认证还是外部应用认证)
*
* @return
*/
private String getAuthType(ServerHttpRequest request) {
//String requestUri = request.getPath().pathWithinApplication().value();
// 是否在外部认证列表中(外部认证列表中的url,是对外部应用开放的,只有在这个列表中的url才对外部应用开放)
//boolean inExtAuthPaths = false;
//
//for (String url : cpProperty.getExternalAuthUrls()) {
// if (antPathMatcher.match(url, requestUri)) {
// inExtAuthPaths = true;
// }
//}
String authType = ServerHttpRequestUtils.getRequestParam(request, RequestParamKeys.AUTH_TYPE);
if (StringUtils.isNotBlank(authType) && AuthTypes.TAKE_TOKEN.equals(authType)) {
return AuthTypeConstant.AUTH_TYPE_EXTERNAL;
}
boolean needExternal = StringUtils.isNotBlank(request.getHeaders().getFirst(TokenHeaderKeyConstant.ACCESS_TOKEN_HEADER_KEY));
boolean needInternal = StringUtils.isNotBlank(request.getHeaders().getFirst(TokenHeaderKeyConstant.AUTHORIZATION_TOKEN_HEADER_KEY));
if (needExternal && needInternal) {
return AuthTypeConstant.AUTH_TYPE_ALL;
}
if (needExternal) {
// url对外部应用开放,并且头里面有AccessToken,那么走外部应用认证
return AuthTypeConstant.AUTH_TYPE_EXTERNAL;
}
return AuthTypeConstant.AUTH_TYPE_INTERNAL;
}
/**
* 获取事务流水号
*
* @return
*/
public static String generateTransactionSerial() {
String[] letterPool = {"a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n"
, "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"};
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 2; i++) {
sb.append(letterPool[(int) (Math.random() * 25)]);
}
sb.append(System.currentTimeMillis());
return sb.toString();
}
public static class CpAuthConfig { public static class CpAuthConfig {
/** /**
@ -255,12 +55,8 @@ public class CpAuthGatewayFilterFactory extends AbstractGatewayFilterFactory<CpA
} }
} }
protected Mono<Void> response(ServerWebExchange exchange, Object object) { @Override
String json = JSON.toJSONString(object); public GatewayFilter apply(CpAuthConfig config) {
DataBuffer buffer = exchange.getResponse().bufferFactory().wrap(json.getBytes(StandardCharsets.UTF_8)); return new EpmetGatewayFilter(config, internalAuthProcessor, externalAuthProcessor);
exchange.getResponse().getHeaders().setContentType(MediaType.APPLICATION_JSON_UTF8);
exchange.getResponse().setStatusCode(HttpStatus.OK);
return exchange.getResponse().writeWith(Flux.just(buffer));
} }
} }

263
epmet-gateway/src/main/java/com/epmet/filter/EpmetGatewayFilter.java

@ -0,0 +1,263 @@
package com.epmet.filter;
import com.alibaba.fastjson.JSON;
import com.epmet.auth.ExternalAuthProcessor;
import com.epmet.auth.InternalAuthProcessor;
import com.epmet.commons.tools.constant.AppClientConstant;
import com.epmet.commons.tools.exception.EpmetErrorCode;
import com.epmet.commons.tools.exception.ExceptionUtils;
import com.epmet.commons.tools.exception.RenException;
import com.epmet.commons.tools.utils.IpUtils;
import com.epmet.commons.tools.utils.Result;
import com.epmet.constant.AuthTypeConstant;
import com.epmet.constant.TokenHeaderKeyConstant;
import com.epmet.openapi.constant.AuthTypes;
import com.epmet.openapi.constant.RequestParamKeys;
import com.epmet.utils.ServerHttpRequestUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.util.MultiValueMap;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Map;
/**
* @Description Gateway过滤器可用于认证请求相关日志打印
* @author wxz
* @date 2021.10.12 09:10:02
*/
@Slf4j
public class EpmetGatewayFilter implements GatewayFilter {
private CpAuthGatewayFilterFactory.CpAuthConfig config;
private InternalAuthProcessor internalAuthProcessor;
private ExternalAuthProcessor externalAuthProcessor;
public EpmetGatewayFilter(CpAuthGatewayFilterFactory.CpAuthConfig config,
InternalAuthProcessor internalAuthProcessor,
ExternalAuthProcessor externalAuthProcessor) {
this.config = config;
this.internalAuthProcessor = internalAuthProcessor;
this.externalAuthProcessor = externalAuthProcessor;
}
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
try {
if (!config.isEnabled()) {
return chain.filter(exchange);
}
return doFilter(exchange, chain);
} catch (RenException re) {
// 人为抛出,则携带错误码和错误信息响应给前端
log.error("EpmetGatewayFilter认证出错RenException,错误信息:{}", ExceptionUtils.getErrorStackTrace(re));
return response(exchange, new Result<>().error(re.getCode(), re.getMessage()));
} catch (Exception t) {
// 其他非人为抛出异常,打印日志,返回"服务器开小差..."给前端
log.error("EpmetGatewayFilter认证出错Exception,错误信息:{}", ExceptionUtils.getErrorStackTrace(t));
return response(exchange, new Result<>().error(EpmetErrorCode.SERVER_ERROR.getCode()));
}
}
/**
* @description 执行请求过滤和验证
*
* @param exchange
* @param chain
* @return
* @author wxz
* @date 2021.10.12 09:16:15
*/
private Mono<Void> doFilter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
//0.添加流水号
String tranSerial = getTranserialFromRequestHeader(request);
if (StringUtils.isBlank(tranSerial)) {
tranSerial = generateTransactionSerial();
// 设置当前线程名
request.mutate().header(AppClientConstant.TRANSACTION_SERIAL_KEY, new String[]{tranSerial});
}
Thread.currentThread().setName(tranSerial);
//1.打印请求信息
logRequest(request);
//2.获取请求路径,参数
String requestUri = request.getPath().pathWithinApplication().value();
MultiValueMap<String, String> queryParams = request.getQueryParams();
String queryParamsStr = convertQueryParams2String(queryParams);
log.info("当前requestUri=[" + requestUri.concat(queryParamsStr) + "],客户端Id:{}", IpUtils.getClientIp(request));
//3.认证
String authType = getAuthType(request);
log.info("认证类型为:{}", authType);
switch (authType) {
case AuthTypeConstant.AUTH_TYPE_ALL:
externalAuthProcessor.auth(exchange, chain);
internalAuthProcessor.auth(exchange, chain);
break;
case AuthTypeConstant.AUTH_TYPE_EXTERNAL:
externalAuthProcessor.auth(exchange, chain);
break;
case AuthTypeConstant.AUTH_TYPE_INTERNAL:
internalAuthProcessor.auth(exchange, chain);
break;
}
return chain.filter(exchange);
}
/**
* @param exchange
* @param object
* @return
* @description 产生Response对象
* @author wxz
* @date 2021.10.12 09:00:20
*/
protected Mono<Void> response(ServerWebExchange exchange, Object object) {
String json = JSON.toJSONString(object);
DataBuffer buffer = exchange.getResponse().bufferFactory().wrap(json.getBytes(StandardCharsets.UTF_8));
exchange.getResponse().getHeaders().setContentType(MediaType.APPLICATION_JSON_UTF8);
exchange.getResponse().setStatusCode(HttpStatus.OK);
return exchange.getResponse().writeWith(Flux.just(buffer));
}
/**
* @param request 请求
* @return void
* @Description 打印请求头
* @author wxz
* @date 2021.08.18 11:18:00
*/
private void logRequest(ServerHttpRequest request) {
HttpHeaders headers = request.getHeaders();
StringBuilder sb = new StringBuilder("请求头:");
for (Map.Entry<String, List<String>> entry : headers.entrySet()) {
String headerKey = entry.getKey();
List<String> headerValue = entry.getValue();
sb.append(headerKey).append(":").append(headerValue).append(",");
}
log.info(sb.toString());
}
/**
* @param request
* @return java.lang.String
* @Description 从request请求头中获取传递过来的流水号
* @author wxz
* @date 2021.08.18 15:55:30
*/
private String getTranserialFromRequestHeader(ServerHttpRequest request) {
List<String> tranSerials = request.getHeaders().get(AppClientConstant.TRANSACTION_SERIAL_KEY);
return CollectionUtils.isEmpty(tranSerials) ? null : tranSerials.get(0);
}
/**
* @return
* @Description 将url参数转化为String
* @author wxz
* @date 2021.08.11 23:12
*/
private String convertQueryParams2String(MultiValueMap<String, String> queryParams) {
try {
if (queryParams == null || queryParams.size() == 0) {
return "";
}
StringBuilder sb = new StringBuilder("");
queryParams.entrySet().forEach(entry -> {
String key = entry.getKey();
List<String> values = entry.getValue();
String value = "";
if (values != null && values.size() > 0) {
value = values.get(0);
}
sb.append("&").append(key).append("=").append(value);
});
String result = sb.toString();
if (result.startsWith("&")) {
result = result.substring(1);
return "?".concat(result);
}
return "";
} catch (Exception e) {
log.warn("gateway中将url参数转化为String失败,程序继续执行,错误信息:".concat(ExceptionUtils.getErrorStackTrace(e)));
return "";
}
}
/**
* 判断需要执行的认证方式(内部应用认证还是外部应用认证)
*
* @return
*/
private String getAuthType(ServerHttpRequest request) {
//String requestUri = request.getPath().pathWithinApplication().value();
// 是否在外部认证列表中(外部认证列表中的url,是对外部应用开放的,只有在这个列表中的url才对外部应用开放)
//boolean inExtAuthPaths = false;
//
//for (String url : cpProperty.getExternalAuthUrls()) {
// if (antPathMatcher.match(url, requestUri)) {
// inExtAuthPaths = true;
// }
//}
String authType = ServerHttpRequestUtils.getRequestParam(request, RequestParamKeys.AUTH_TYPE);
if (StringUtils.isNotBlank(authType) && AuthTypes.TAKE_TOKEN.equals(authType)) {
return AuthTypeConstant.AUTH_TYPE_EXTERNAL;
}
boolean needExternal = StringUtils.isNotBlank(request.getHeaders().getFirst(TokenHeaderKeyConstant.ACCESS_TOKEN_HEADER_KEY));
boolean needInternal = StringUtils.isNotBlank(request.getHeaders().getFirst(TokenHeaderKeyConstant.AUTHORIZATION_TOKEN_HEADER_KEY));
if (needExternal && needInternal) {
return AuthTypeConstant.AUTH_TYPE_ALL;
}
if (needExternal) {
// url对外部应用开放,并且头里面有AccessToken,那么走外部应用认证
return AuthTypeConstant.AUTH_TYPE_EXTERNAL;
}
return AuthTypeConstant.AUTH_TYPE_INTERNAL;
}
/**
* 获取事务流水号
*
* @return
*/
public String generateTransactionSerial() {
String[] letterPool = {"a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n"
, "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"};
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 2; i++) {
sb.append(letterPool[(int) (Math.random() * 25)]);
}
sb.append(System.currentTimeMillis());
return sb.toString();
}
}

27
epmet-gateway/src/main/java/com/epmet/healthcheck/HealthCheckController.java

@ -0,0 +1,27 @@
package com.epmet.healthcheck;
import com.epmet.commons.tools.utils.Result;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @Description 健康检查gateway中的/api前缀只用于匹配转发路由自身的Controller的前缀为/
* @author wxz
* @date 2021.10.11 17:56:34
*/
@RestController
@RequestMapping("/gateway/healthcheck")
public class HealthCheckController {
/**
* http健康检查
* @return
*/
@PostMapping("http")
public Result httpHealthCheck() {
return new Result();
}
}
Loading…
Cancel
Save