forked from rongchao/epmet-cloud-rizhao
3 changed files with 295 additions and 209 deletions
@ -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(); |
|||
} |
|||
} |
@ -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…
Reference in new issue