001package top.cenze.utils.http.request; 002 003import cn.hutool.core.collection.CollectionUtil; 004import cn.hutool.core.convert.Convert; 005import cn.hutool.core.util.ObjectUtil; 006import cn.hutool.core.util.StrUtil; 007import com.alibaba.fastjson.JSON; 008import com.alibaba.fastjson.JSONObject; 009import io.netty.buffer.ByteBufAllocator; 010import lombok.extern.slf4j.Slf4j; 011import org.springframework.cloud.gateway.filter.GatewayFilterChain; 012import org.springframework.core.io.buffer.DataBuffer; 013import org.springframework.core.io.buffer.DataBufferUtils; 014import org.springframework.core.io.buffer.NettyDataBufferFactory; 015import org.springframework.http.HttpHeaders; 016import org.springframework.http.HttpMethod; 017import org.springframework.http.MediaType; 018import org.springframework.http.server.reactive.ServerHttpRequest; 019import org.springframework.http.server.reactive.ServerHttpRequestDecorator; 020import org.springframework.web.server.ServerWebExchange; 021import org.springframework.web.util.UriComponentsBuilder; 022import reactor.core.publisher.Flux; 023import reactor.core.publisher.Mono; 024import top.cenze.utils.aspect.AspectUtil; 025 026import java.net.InetAddress; 027import java.net.URI; 028import java.net.UnknownHostException; 029import java.nio.CharBuffer; 030import java.nio.charset.StandardCharsets; 031import java.util.HashMap; 032import java.util.Map; 033import java.util.concurrent.atomic.AtomicReference; 034 035@Slf4j 036public class GatewayUtil { 037 // 多次反向代理后会有多个ip值 的分割符 038 private final static String IP_UTILS_FLAG = ","; 039 // 未知IP 040 private final static String UNKNOWN = "unknown"; 041 // 本地 IP 042 private final static String LOCALHOST_IP = "0:0:0:0:0:0:0:1"; 043 private final static String LOCALHOST_IP1 = "127.0.0.1"; 044 045 /** 046 * 获取TOKEN 047 * @param exchange 048 * @return 049 */ 050 public static String getToken(ServerWebExchange exchange) { 051 // 获取请求 052 ServerHttpRequest request = exchange.getRequest(); 053 054 String token = request.getHeaders().getFirst("TOKEN"); 055 if (StrUtil.isEmpty(token)) { 056 token = getParameter(exchange, "TOKEN"); 057 } 058 059 return token; 060 } 061 062 /** 063 * 获取请求参数 064 * @param exchange 065 * @param name 066 * @return 067 */ 068 public static String getParameter(ServerWebExchange exchange, String name) { 069 // 获取请求 070 ServerHttpRequest request = exchange.getRequest(); 071 072 // 获取方法 073 HttpMethod method = request.getMethod(); 074 075 // POST或PUT方法 076 if (HttpMethod.POST.equals(method) || HttpMethod.PUT.equals(method)) { 077 String body = resolveBodyFromRequest(request); 078 JSONObject json = null; 079 if (StrUtil.isNotEmpty(body)) { 080 json = JSON.parseObject(body); 081 } else { 082 json = new JSONObject(); 083 } 084 log.info("getParameter json: {}", json.toJSONString()); 085 086 if (ObjectUtil.isNotNull(json) && ObjectUtil.isNotNull(json.get(name))) { 087 return Convert.toStr(json.get(name)); 088 } 089 } 090 // GET方法 091 else if (HttpMethod.GET.equals(method)) { 092 return request.getQueryParams().getFirst(name); 093 } 094 095 return null; 096 } 097 098 /** 099 * 设置请求参数 100 * 调用此方法后,需要在调用rewriteRequestParams重写request 101 * @param exchange 102 * @param chain 103 * @param name 104 * @param value 105 * @return 106 */ 107 public static String setParameters(ServerWebExchange exchange, GatewayFilterChain chain, String name, Object value) { 108 Map<String, Object> mapParams = new HashMap<>(); 109 mapParams.put(name, value); 110 111 return setParameters(exchange, chain, mapParams); 112 } 113 114 /** 115 * 设置请求参数集合 116 * 调用此方法后,需要在调用rewriteRequestParams重写request 117 * @param exchange 118 * @param chain 119 * @param mapParams 120 * @return 121 */ 122 public static String setParameters(ServerWebExchange exchange, GatewayFilterChain chain, Map<String, Object> mapParams) { 123 // 获取请求 124 ServerHttpRequest request = exchange.getRequest(); 125 // 获取方法 126 HttpMethod method = request.getMethod(); 127 // 参数文本类型 128 String contentType = request.getHeaders().getFirst(HttpHeaders.CONTENT_TYPE); 129 130 // POST、JSON参数、或表单格式(如上传文件等) 131 if (HttpMethod.POST.equals(method) && 132 (MediaType.APPLICATION_FORM_URLENCODED_VALUE.equalsIgnoreCase(contentType) 133 || MediaType.APPLICATION_JSON_VALUE.equalsIgnoreCase(contentType))) { 134 // 获取请求体字符串 135 String strBody = resolveBodyFromRequest(request); 136 if (StrUtil.isEmpty(strBody)) { 137 return null; 138 } 139 140 // application/x-www-form-urlencoded 141 // 其他上传文件之类的,不做参数处理,因为文件流添加参数,文件原格式就会出问题了 142 if (MediaType.APPLICATION_FORM_URLENCODED_VALUE.equalsIgnoreCase(contentType)) { 143 // 普通键值对,增加参数 144 if (CollectionUtil.isNotEmpty(mapParams)) { 145 StringBuilder sb = new StringBuilder(); 146 for (Map.Entry<String, Object> entry : mapParams.entrySet()) { 147 sb.append("&").append(entry.getKey()).append("=").append(entry.getValue()); 148 } 149 150 return strBody + sb.toString(); 151 } 152 } 153 // application/json 154 else if (MediaType.APPLICATION_JSON_VALUE.equalsIgnoreCase(contentType)) { 155 JSONObject json = null; 156 if (StrUtil.isNotEmpty(strBody)) { 157 json = JSON.parseObject(strBody); 158 } else { 159 json = new JSONObject(); 160 } 161 log.info("getParameter json: {}", json.toJSONString()); 162 163 // 添加/修改参数 164 if (CollectionUtil.isNotEmpty(mapParams)) { 165 for (Map.Entry<String, Object> entry : mapParams.entrySet()) { 166 json.put(entry.getKey(), entry.getValue()); 167 } 168 169 return json.toJSONString(); 170 } 171 } 172 } 173 // GET方法 174 else if (HttpMethod.GET.equals(method)) { 175 // 获取原参数 176 URI uri = request.getURI(); 177 StringBuilder query = new StringBuilder(); 178 String originalQuery = uri.getRawQuery(); 179 if (org.springframework.util.StringUtils.hasText(originalQuery)) { 180 query.append(originalQuery); 181 if (originalQuery.charAt(originalQuery.length() - 1) != '&') { 182 query.append('&'); 183 } 184 } 185 // 添加查询参数 186 if (CollectionUtil.isNotEmpty(mapParams)) { 187 StringBuilder sb = new StringBuilder(); 188 for (Map.Entry<String, Object> entry : mapParams.entrySet()) { 189 query.append("&").append(entry.getKey()).append("=").append(entry.getValue()); 190 } 191 192 return query.toString(); 193 } 194 } 195 196 return null; 197 } 198 199 /** 200 * 重写请求参数 201 * @param exchange 202 * @param chain 203 * @param strBody 204 * @return 205 */ 206 public static Mono<Void> rewriteRequestParams(ServerWebExchange exchange, GatewayFilterChain chain, String strBody) { 207 if (StrUtil.isEmpty(strBody)) { 208 return chain.filter(exchange); 209 } 210 211 // 获取请求 212 ServerHttpRequest request = exchange.getRequest(); 213 // 获取方法 214 HttpMethod method = request.getMethod(); 215 // 参数文本类型 216 String contentType = request.getHeaders().getFirst(HttpHeaders.CONTENT_TYPE); 217 // 获取原uri 218 URI uri = request.getURI(); 219 220 // POST、JSON参数、或表单格式(如上传文件等) 221 if (HttpMethod.POST.equals(method) && 222 (MediaType.APPLICATION_FORM_URLENCODED_VALUE.equalsIgnoreCase(contentType) 223 || MediaType.APPLICATION_JSON_VALUE.equalsIgnoreCase(contentType))) { 224 //下面的将请求体再次封装写回到request里,传到下一级,否则,由于请求体已被消费,后续的服务将取不到值 225 URI newUri = UriComponentsBuilder.fromUri(uri).build(true).toUri(); 226 request = exchange.getRequest().mutate().uri(newUri).build(); 227 DataBuffer dataBuffer = stringToDataBuffer(strBody); 228 Flux<DataBuffer> bodyFlux = Flux.just(dataBuffer); 229 230 // 定义新的消息头 231 HttpHeaders headers = new HttpHeaders(); 232 headers.putAll(exchange.getRequest().getHeaders()); 233 234 // 由于修改了传递参数,需要重新设置CONTENT_LENGTH,长度是字节长度,不是字符串长度 235 int length = strBody.getBytes().length; 236 headers.remove(HttpHeaders.CONTENT_LENGTH); 237 headers.setContentLength(length); 238 239 // 设置CONTENT_TYPE 240 if (StrUtil.isNotEmpty(contentType)) { 241 headers.set(HttpHeaders.CONTENT_TYPE, contentType); 242 } 243 244 // 由于post的body只能订阅一次,由于上面代码中已经订阅过一次body。所以要再次封装请求到request才行,不然会报错请求已经订阅过 245 request = new ServerHttpRequestDecorator(request) { 246 @Override 247 public HttpHeaders getHeaders() { 248 long contentLength = headers.getContentLength(); 249 HttpHeaders httpHeaders = new HttpHeaders(); 250 httpHeaders.putAll(super.getHeaders()); 251 if (contentLength > 0) { 252 httpHeaders.setContentLength(contentLength); 253 } else { 254 // TODO: this causes a 'HTTP/1.1 411 Length Required' on httpbin.org 255 httpHeaders.set(HttpHeaders.TRANSFER_ENCODING, "chunked"); 256 } 257 return httpHeaders; 258 } 259 260 @Override 261 public Flux<DataBuffer> getBody() { 262 return bodyFlux; 263 } 264 }; 265 266 //封装request,传给下一级 267 request.mutate().header(HttpHeaders.CONTENT_LENGTH, Integer.toString(strBody.length())); 268 return chain.filter(exchange.mutate().request(request).build()); 269 } 270 // GET方法 271 else if (HttpMethod.GET.equals(method)) { 272 // 替换查询参数 273 URI newUri = UriComponentsBuilder.fromUri(uri) 274 .replaceQuery(strBody) 275 .build(true) 276 .toUri(); 277 278 request = exchange.getRequest().mutate().uri(newUri).build(); 279 return chain.filter(exchange.mutate().request(request).build()); 280 } 281 282 return chain.filter(exchange); 283 } 284 285 /** 286 * 从Flux<DataBuffer>中获取字符串的方法 287 * @return 请求体 288 */ 289 private static String resolveBodyFromRequest(ServerHttpRequest request) { 290 //获取请求体 291 Flux<DataBuffer> body = request.getBody(); 292 293 AtomicReference<String> bodyRef = new AtomicReference<>(); 294 body.subscribe(buffer -> { 295 CharBuffer charBuffer = StandardCharsets.UTF_8.decode(buffer.asByteBuffer()); 296 DataBufferUtils.release(buffer); 297 bodyRef.set(charBuffer.toString()); 298 }); 299 300 //获取request body 301 return bodyRef.get(); 302 } 303 304 /** 305 * 字符串转DataBuffer 306 * @param value 307 * @return 308 */ 309 private static DataBuffer stringToDataBuffer(String value) { 310 byte[] bytes = value.getBytes(StandardCharsets.UTF_8); 311 NettyDataBufferFactory nettyDataBufferFactory = new NettyDataBufferFactory(ByteBufAllocator.DEFAULT); 312 DataBuffer buffer = nettyDataBufferFactory.allocateBuffer(bytes.length); 313 buffer.write(bytes); 314 return buffer; 315 } 316 317 /** 318 * 获取访问者真实ip 319 * @param exchange 320 * @return 321 */ 322 public static String getRemoteIP(ServerWebExchange exchange) { 323 // 获取请求 324 ServerHttpRequest request = exchange.getRequest(); 325 326 return getRemoteIP(request); 327 } 328 329 /** 330 * 获取访问者真实ip 331 * @param request 332 * @return 333 */ 334 public static String getRemoteIP(ServerHttpRequest request) { 335 String ip = null; 336 try { 337 // 以下两个获取在k8s中,真实的客户端IP,放到了x-Original-Forwarded-For。而WAF的回源地址放到了 x-Forwarded-For了。 338 ip = request.getHeaders().getFirst("X-Original-Forwarded-For"); 339 if (StrUtil.isEmpty(ip) || UNKNOWN.equalsIgnoreCase(ip)) { 340 ip = request.getHeaders().getFirst("X-Forwarded-For"); 341 } 342 // 获取nginx等代理的ip 343 if (StrUtil.isEmpty(ip) || UNKNOWN.equalsIgnoreCase(ip)) { 344 ip = request.getHeaders().getFirst("x-forwarded-for"); 345 } 346 if (StrUtil.isEmpty(ip) || UNKNOWN.equalsIgnoreCase(ip)) { 347 ip = request.getHeaders().getFirst("Proxy-Client-IP"); 348 } 349 if (StrUtil.isEmpty(ip) || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) { 350 ip = request.getHeaders().getFirst("WL-Proxy-Client-IP"); 351 } 352 if (StrUtil.isEmpty(ip) || UNKNOWN.equalsIgnoreCase(ip)) { 353 ip = request.getHeaders().getFirst("HTTP_CLIENT_IP"); 354 } 355 if (StrUtil.isEmpty(ip) || UNKNOWN.equalsIgnoreCase(ip)) { 356 ip = request.getHeaders().getFirst("HTTP_X_FORWARDED_FOR"); 357 } 358 if (StrUtil.isEmpty(ip) || UNKNOWN.equalsIgnoreCase(ip)) { 359 ip = request.getHeaders().getFirst("X-Real-IP"); 360 } 361 362 // 兼容k8s集群获取ip 363 if (StrUtil.isEmpty(ip) || UNKNOWN.equalsIgnoreCase(ip)) { 364 ip = request.getRemoteAddress().getAddress().getHostAddress(); 365 if (LOCALHOST_IP1.equalsIgnoreCase(ip) || LOCALHOST_IP.equalsIgnoreCase(ip)) { 366 //根据网卡取本机配置的IP 367 InetAddress iNet = null; 368 try { 369 iNet = InetAddress.getLocalHost(); 370 } catch (UnknownHostException e) { 371 log.error("getClientIp error: ", e); 372 } 373 ip = iNet.getHostAddress(); 374 } 375 } 376 } catch (Exception e) { 377 log.error("IPUtils ERROR ", e); 378 } 379 380 // 使用代理,则获取第一个IP地址 381 if (StrUtil.isNotEmpty(ip) && ip.indexOf(IP_UTILS_FLAG) > 0) { 382 ip = ip.substring(0, ip.indexOf(IP_UTILS_FLAG)); 383 } 384 return ip; 385 } 386 387 /** 388 * 获取访问域名 389 * @param exchange 390 * @return 391 */ 392 public static String getServerName(ServerWebExchange exchange) { 393 // 获取请求 394 ServerHttpRequest request = exchange.getRequest(); 395 396 return getServerName(request); 397 } 398 399 /** 400 * 获取访问域名 401 * @param request 402 * @return 403 */ 404 public static String getServerName(ServerHttpRequest request) { 405 return request.getURI().getHost(); 406 } 407}