# SpringBoot过滤器和拦截器
SpringBoot过滤器和拦截器(Filter-And-Interceptor)
# 1. 过滤器
待补充
# 2. 拦截器
待补充
# 3. 日志过滤
记录一下日志输入输出
# 3.1. WebMvc
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.text.AntPathMatcher;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.pcic.util.AddrUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpHeaders;
import org.springframework.stereotype.Component;
import org.springframework.web.util.WebUtils;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ReadListener;
import javax.servlet.ServletException;
import javax.servlet.ServletInputStream;
import javax.servlet.ServletOutputStream;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.WriteListener;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 请求响应日志过滤器
*
* @author wliduo[i@dolyw.com]
* @date 2022/5/24 9:18
*/
@Component
@WebFilter(filterName = "RequestLogFilter", urlPatterns = "/**")
public class RequestLogFilter implements Filter {
private static final Logger logger = LoggerFactory.getLogger(RequestLogFilter.class);
/**
* 忽略URL
*/
private static final List<String> IGNORE_URL = new ArrayList<String>(){{
add("/favicon.ico");
add("/doc.html");
add("/webjars/**");
add("/v3/api-docs/**");
add("/druid/**");
}};
private static final String APPLICATION_X_WWW_FORM_URLENCODED = "application/x-www-form-urlencoded";
private static final String MULTIPART_FORM_DATA = "multipart/form-data";
private static final String LOGIN_USER_ID_KEY = "LOGIN_USER_ID";
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
long timeBegin = System.currentTimeMillis();
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
// 请求属性获取
String reqStr = "", respStr = "";
String ip = AddrUtil.getRemoteAddr(request);
String url = request.getRequestURI(), method = request.getMethod().toUpperCase();
String contentType = request.getHeader(HttpHeaders.CONTENT_TYPE);
// contentType = contentType != null ? contentType.toLowerCase() : APPLICATION_X_WWW_FORM_URLENCODED;
Object userId = WebUtils.getSessionAttribute(request, LOGIN_USER_ID_KEY);
String sessionId = WebUtils.getSessionId(request);
// 忽略URL跳过
if (UrlUtils.match(url, IGNORE_URL)) {
// logger.info("【日志统一过滤】入口:{\"ip\":\"{}\", \"url\":\"{}\", \"user\":\"{}\", \"id\":\"{}\"}", ip, url, userId, sessionId);
filterChain.doFilter(servletRequest, servletResponse);
} else {
// 获取请求中的流取出来的字符串,再次转换成流,然后把它放入到新对象中
RequestWrapper requestWrapper = new RequestWrapper(request);
ResponseWrapper responseWrapper = new ResponseWrapper(response);
// GET及application/x-www-form-urlencoded,multipart/form-data直接从Parameter获取参数
Map paramMap = new HashMap(16);
Enumeration paramNames = request.getParameterNames();
while (paramNames.hasMoreElements()) {
String paramName = (String) paramNames.nextElement();
String[] paramValues = request.getParameterValues(paramName);
if (paramValues.length == 1) {
String paramValue = paramValues[0];
if (paramValue.length() != 0) {
paramMap.put(paramName, paramValue);
}
}
}
// paramMap为空取Body
if (paramMap.isEmpty()) {
reqStr = requestWrapper.getBody();
} else {
reqStr = JSONUtil.toJsonStr(paramMap);
}
logger.info("【日志统一过滤】入口:{\"ip\":\"{}\", \"url\":\"{}\"},请求参数:{}", ip, url, reqStr);
filterChain.doFilter(requestWrapper, responseWrapper);
respStr = responseWrapper.getBody();
// 将返回值重新写入Response
response.getOutputStream().write(respStr.getBytes());
long timeEnd = System.currentTimeMillis();
logger.info("【日志统一过滤】出口:{\"ip\":\"{}\", \"url\":\"{}\", \"time\":\"{}s\"},请求参数:{},返回参数:{}", ip, url, (timeEnd - timeBegin) / 1000.0D, reqStr, respStr);
}
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void destroy() {
}
/**
* RequestWrapper
*
* @author wliduo[i@dolyw.com]
* @date 2022/5/24 9:37
*/
public static class RequestWrapper extends HttpServletRequestWrapper {
private final String body;
public RequestWrapper(HttpServletRequest request) throws IOException {
super(request);
StringBuilder stringBuilder = new StringBuilder("");
try (InputStreamReader inputStreamReader = new InputStreamReader(request.getInputStream());
BufferedReader bufferedReader = new BufferedReader(inputStreamReader)) {
// 不能使用byte,byte一个字节无法兼容中文字符会导致汉字乱码,char两个字节支持单个中文字符
char[] charBuffer = new char[128];
int bytesRead = -1;
while ((bytesRead = bufferedReader.read(charBuffer)) > 0) {
stringBuilder.append(charBuffer, 0, bytesRead);
}
} finally {
body = stringBuilder.toString();
}
}
@Override
public ServletInputStream getInputStream() throws IOException {
final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(body.getBytes());
ServletInputStream servletInputStream = new ServletInputStream() {
@Override
public boolean isFinished() {
return false;
}
@Override
public boolean isReady() {
return false;
}
@Override
public void setReadListener(ReadListener readListener) {
}
@Override
public int read() throws IOException {
return byteArrayInputStream.read();
}
};
return servletInputStream;
}
public String getBody() {
return this.body;
}
}
/**
* ResponseWrapper
*
* @author wliduo[i@dolyw.com]
* @date 2022/5/24 9:37
*/
public static class ResponseWrapper extends HttpServletResponseWrapper {
private PrintWriter printWriter;
private ByteArrayOutputStream bytes = new ByteArrayOutputStream();
public ResponseWrapper(HttpServletResponse response) {
super(response);
}
@Override
public ServletOutputStream getOutputStream() throws IOException {
// 将数据写到byte中
return new CustomServletOutputStream(bytes);
}
/**
* 重写父类的getWriter()方法,将响应数据缓存在PrintWriter中
*/
@Override
public PrintWriter getWriter() throws IOException {
try (OutputStreamWriter outputStreamWriter = new OutputStreamWriter(bytes, "utf-8")) {
printWriter = new PrintWriter(outputStreamWriter);
}
return printWriter;
}
/**
* 获取缓存在PrintWriter中的响应数据
*
* @return
*/
public byte[] getBytes() {
if (printWriter != null) {
printWriter.close();
return bytes.toByteArray();
}
if (bytes != null) {
try {
bytes.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
return bytes.toByteArray();
}
public String getBody() throws IOException {
return new String(this.getBytes(), "utf-8");
}
class CustomServletOutputStream extends ServletOutputStream {
private ByteArrayOutputStream byteArrayOutputStream;
public CustomServletOutputStream(ByteArrayOutputStream byteArrayOutputStream) {
this.byteArrayOutputStream = byteArrayOutputStream;
}
@Override
public void write(int b) throws IOException {
// 将数据写到stream中
byteArrayOutputStream.write(b);
}
@Override
public boolean isReady() {
return false;
}
@Override
public void setWriteListener(WriteListener listener) {
}
}
}
}
# 3.2. WebFlux
package com.pcic.filter;
import cn.hutool.core.text.StrSplitter;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.Header;
import com.alibaba.fastjson.JSON;
import com.pcic.util.AddrUtil;
import com.pcic.util.UrlUtils;
import lombok.extern.slf4j.Slf4j;
import org.reactivestreams.Publisher;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.factory.rewrite.CachedBodyOutputMessage;
import org.springframework.cloud.gateway.support.BodyInserterContext;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.codec.ServerCodecConfigurer;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpRequestDecorator;
import org.springframework.http.server.reactive.ServerHttpResponseDecorator;
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 org.springframework.web.server.WebFilter;
import org.springframework.web.server.WebFilterChain;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
/**
* RequestLogFilter - WebFilter过滤
*
* WebFlux必须使用@Bean方式才没问题
* 使用@Component会出现上下文混乱
*
* https://blog.csdn.net/qq_33803102/article/details/110927078
* https://segmentfault.com/a/1190000016229552
* https://www.modb.pro/db/170295
*
* @author wliduo[i@dolyw.com]
* @date 2022/8/4 10:06
*/
@Slf4j
@Order(Ordered.HIGHEST_PRECEDENCE)
public class RequestLogFilter implements WebFilter {
/**
* TRACE_ID
*/
public static final String TRACE_ID = "TraceId";
/**
* StartTime
*/
public static final String START_TIME = "StartTime";
/**
* Path
*/
public static final String PATH = "Path";
/**
* URL
*/
public static final String URL = "Url";
/**
* IP
*/
public static final String IP = "IP";
/**
* RequestData
*/
public static final String REQUEST_DATA = "RequestData";
/**
* ResponseData
*/
public static final String RESPONSE_DATA = "ResponseData";
/**
* WebKitFormBoundary
*/
public static final String WEBKIT_FORM_BOUNDARY = "WebKitFormBoundary";
/**
* 忽略URL
*/
private static final List<String> IGNORE_URL = new ArrayList<String>() {{
add("/favicon.ico");
add("/doc.html");
add("/webjars/**");
add("/v3/api-docs/**");
add("/druid/**");
add("/login/current");
add("/login/permissions");
add("/web/adapt/common/download**");
}};
@Autowired
private AuthEncryptFilter authEncryptFilter;
@Autowired
private ServerCodecConfigurer serverCodecConfigurer;
@Override
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
Mono<Void> mono = null;
// 请求ID
exchange.getAttributes().put(TRACE_ID, RandomUtil.randomString(16));
// 开始时间
exchange.getAttributes().put(START_TIME, System.currentTimeMillis());
// 请求IP
exchange.getAttributes().put(IP, AddrUtil.getIpAddress(exchange.getRequest()));
// 请求URL
String path = exchange.getRequest().getURI().getRawPath();
String query = exchange.getRequest().getURI().getRawQuery();
String url = StrUtil.isNotBlank(query) ? path + "?" + query : path;
exchange.getAttributes().put(PATH, path);
exchange.getAttributes().put(URL, url);
// 忽略URL跳过
if (UrlUtils.match(url, IGNORE_URL)) {
// logger.info("【日志统一过滤】入口:{\"ip\":\"{}\", \"url\":\"{}\", \"user\":\"{}\", \"id\":\"{}\"}", ip, url, userId, sessionId);
return chain.filter(exchange);
} else {
// Method
String method = exchange.getRequest().getMethodValue();
// ContentType
MediaType contentType = exchange.getRequest().getHeaders().getContentType();
contentType = contentType != null ? contentType : MediaType.APPLICATION_JSON;
// GET请求直接处理
if (HttpMethod.GET.toString().equals(method)) {
Map<String, String> queryParams = exchange.getRequest().getQueryParams().toSingleValueMap();
exchange.getAttributes().put(REQUEST_DATA, JSON.toJSONString(queryParams));
RequestLogFilter.log(exchange, true);
mono = chain.filter(exchange.mutate().response(this.readResponse(exchange)).build());
} else if (MediaType.APPLICATION_FORM_URLENCODED.equals(contentType)
|| contentType.toString().startsWith(MediaType.MULTIPART_FORM_DATA_VALUE)) {
mono = this.readData(exchange, chain);
} else {
mono = this.readBody(exchange, chain);
}
return mono.then(Mono.fromRunnable(() -> RequestLogFilter.log(exchange, false)));
}
}
/**
* 读取Body数据
*
* @param exchange
* @param chain
* @return reactor.core.publisher.Mono<java.lang.Void>
* @throws
* @author wliduo[i@dolyw.com]
* @date 2022/8/4 15:12
*/
private Mono<Void> readBody(ServerWebExchange exchange, WebFilterChain chain) {
// ServerRequest serverRequest = ServerRequest.create(exchange, HandlerStrategies.withDefaults().messageReaders());
// 使用默认参数缓冲区只能256K,切换为自定义大小 spring.codec.max-in-memory-size = 16MB
ServerRequest serverRequest = ServerRequest.create(exchange, serverCodecConfigurer.getReaders());
Mono<String> modifiedBody = serverRequest.bodyToMono(String.class).flatMap(body -> {
// 重写数据
exchange.getAttributes().put(REQUEST_DATA, body);
RequestLogFilter.log(exchange, true);
// 解密处理
String content = authEncryptFilter.dataDecrypt(exchange);
if (StrUtil.isNotBlank(content)) {
return Mono.just(content);
}
return Mono.just(body);
});
BodyInserter bodyInserter = BodyInserters.fromPublisher(modifiedBody, String.class);
HttpHeaders headers = new HttpHeaders();
headers.putAll(exchange.getRequest().getHeaders());
headers.remove(HttpHeaders.CONTENT_LENGTH);
CachedBodyOutputMessage outputMessage = new CachedBodyOutputMessage(exchange, headers);
Mono<Void> mono = bodyInserter.insert(outputMessage, new BodyInserterContext());
return mono.then(Mono.defer(() -> {
ServerHttpRequestDecorator newRequest = new ServerHttpRequestDecorator(exchange.getRequest()) {
@Override
public HttpHeaders getHeaders() {
long contentLength = headers.getContentLength();
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.putAll(super.getHeaders());
if (contentLength > 0) {
httpHeaders.setContentLength(contentLength);
} else {
httpHeaders.set(HttpHeaders.TRANSFER_ENCODING, "chunked");
}
return httpHeaders;
}
@Override
public Flux<DataBuffer> getBody() {
return outputMessage.getBody();
}
};
return chain.filter(exchange.mutate().request(newRequest).response(this.readResponse(exchange)).build());
}));
}
/**
* 读取Data数据
*
* @param exchange
* @param chain
* @return reactor.core.publisher.Mono<java.lang.Void>
* @throws
* @author wliduo[i@dolyw.com]
* @date 2022/8/4 15:11
*/
private Mono<Void> readData(ServerWebExchange exchange, WebFilterChain chain) {
Mono<DataBuffer> dataBufferMono = DataBufferUtils.join(exchange.getRequest().getBody());
// 请求数据为空不会进入flatMap处理逻辑
Mono<Void> mono = dataBufferMono.flatMap(dataBuffer -> {
byte[] bytes = new byte[dataBuffer.readableByteCount()];
dataBuffer.read(bytes);
try {
String data = new String(bytes, StandardCharsets.UTF_8);
// 是否包含WebKitFormBoundary数据
if (data.contains(WEBKIT_FORM_BOUNDARY)) {
String contentType = exchange.getRequest().getHeaders().getContentType().toString();
String[] contentTypeId = contentType.split(WEBKIT_FORM_BOUNDARY);
List<String> paramList = StrSplitter.split(data, WEBKIT_FORM_BOUNDARY + contentTypeId[1], true, true);
// 循环内移除元素必须使用迭代器遍历
for (Iterator<String> iterator = paramList.iterator(); iterator.hasNext();) {
String param = iterator.next();
if (!param.contains(Header.CONTENT_DISPOSITION.getValue())) {
// 不包含Content-Disposition说明是无效数据
iterator.remove();
}
if (param.contains(Header.CONTENT_DISPOSITION.getValue()) && param.contains(Header.CONTENT_TYPE.getValue())) {
// 包含Content-Type说明是文件参数
iterator.remove();
}
}
exchange.getAttributes().put(REQUEST_DATA, JSON.toJSONString(paramList));
} else {
exchange.getAttributes().put(REQUEST_DATA, data);
}
} catch (Exception e) {
String traceId = exchange.getAttribute(TRACE_ID);
log.warn("读取Data数据解析参数异常,请求轨迹:{}", traceId, e);
}
DataBufferUtils.release(dataBuffer);
Flux<DataBuffer> cachedFlux = Flux.defer(() -> {
DataBuffer buffer = exchange.getResponse().bufferFactory().wrap(bytes);
return Mono.just(buffer);
});
ServerHttpRequest newRequest = new ServerHttpRequestDecorator(exchange.getRequest()) {
@Override
public Flux<DataBuffer> getBody() {
return cachedFlux;
}
};
return chain.filter(exchange.mutate().request(newRequest).response(this.readResponse(exchange)).build());
});
RequestLogFilter.log(exchange, true);
return mono.then(Mono.defer(() -> {
String requestData = exchange.getAttribute(REQUEST_DATA);
if (StrUtil.isNotBlank(requestData)) {
return mono;
} else {
// 请求数据为空,必须走chain.filter处理,否则不会进异常处理,会返回空数据
return chain.filter(exchange.mutate().response(this.readResponse(exchange)).build());
}
}));
}
/**
* 读取返回数据,创建新的Response装饰对象,并传入原始Response对象,然后重写writeWith方法
*
* @param exchange
* @return org.springframework.http.server.reactive.ServerHttpResponseDecorator
* @throws
* @author wliduo[i@dolyw.com]
* @date 2022/8/4 14:18
*/
private ServerHttpResponseDecorator readResponse(ServerWebExchange exchange) {
return new ServerHttpResponseDecorator(exchange.getResponse()) {
@Override
public Mono<Void> writeWith(Publisher<? extends DataBuffer> body) {
Mono<DataBuffer> mono = DataBufferUtils.join(body).map(buffer -> {
try {
// 重写数据
byte[] bytes;
String data = buffer.toString(StandardCharsets.UTF_8);
exchange.getAttributes().put(RESPONSE_DATA, data);
// 加密处理
String content = authEncryptFilter.dataEncrypt(exchange);
if (StrUtil.isNotBlank(content)) {
bytes = content.getBytes(StandardCharsets.UTF_8);
} else {
bytes = data.getBytes(StandardCharsets.UTF_8);
}
getHeaders().setContentLength(bytes.length);
return this.bufferFactory().wrap(bytes);
} finally {
DataBufferUtils.release(buffer);
}
});
return super.writeWith(mono);
}
};
}
/**
* 日志输出
*
* @param exchange
* @param start
* @return void
* @throws
* @author wliduo[i@dolyw.com]
* @date 2022/8/4 14:30
*/
public static void log(ServerWebExchange exchange, boolean start) {
String traceId = exchange.getAttribute(TRACE_ID);
String ip = exchange.getAttribute(IP);
String url = exchange.getAttribute(URL);
String requestData = exchange.getAttribute(REQUEST_DATA);
if (start) {
log.info("【日志统一过滤】入口:{\"ip\":\"{}\", \"url\":\"{}\", \"traceId\":\"{}\"},请求参数:{}", ip, url, traceId, requestData);
} else {
long startTime = exchange.getAttribute(START_TIME);
String responseData = exchange.getAttribute(RESPONSE_DATA);
log.info("【日志统一过滤】出口:{\"ip\":\"{}\", \"url\":\"{}\", \"traceId\":\"{}\", \"time\":\"{}s\"},请求参数:{},返回参数:{}",
ip, url, traceId, (System.currentTimeMillis() - startTime) / 1000.0D, requestData, responseData);
}
}
}
# 3.3. 工具类
package com.pcic.util;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import org.springframework.util.AntPathMatcher;
import java.util.List;
/**
* UrlUtils
*
* @author wliduo[i@dolyw.com]
* @date 2022/10/17 13:47
*/
public class UrlUtils {
/**
* 判断字符是否匹配
* ? 表示单个字符
* * 表示一层路径内的任意字符串,不可跨层级
* ** 表示任意层路径
*
* @param urlPath 需要匹配的url
* @param urls 匹配规则
* @return boolean
* @throws
* @author wliduo[i@dolyw.com]
* @date 2022/5/31 19:41
*/
public static boolean match(String urlPath, List<String> urls) {
if (StrUtil.isEmpty(urlPath) || CollUtil.isEmpty(urls)) {
return false;
}
for (String url : urls) {
AntPathMatcher matcher = new AntPathMatcher();
if (matcher.match(url, urlPath)) {
return true;
}
}
return false;
}
}
package com.pcic.util;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpHeaders;
import org.springframework.http.server.reactive.ServerHttpRequest;
import javax.servlet.http.HttpServletRequest;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Enumeration;
import java.util.Optional;
/**
* 获取IP地址工具
*
* @author wliduo[i@dolyw.com]
* @date 2021/9/9 19:19
*/
@Slf4j
public class AddrUtil {
/**
* unknown
*/
private final static String UNKNOWN_STR = "unknown";
/**
* 127.0.0.1
*/
private final static String LOCALHOST_STR = "127.0.0.1";
/**
* 0:0:0:0:0:0:0:1
*/
private final static String LOCALHOST_IP_STR = "0:0:0:0:0:0:0:1";
/**
* IP_LEN
*/
private static final int IP_LEN = 15;
/**
* 获取客户端IP地址
*/
public static String getRemoteAddr(HttpServletRequest request) {
/*Enumeration paramNames = request.getHeaderNames();
while (paramNames.hasMoreElements()) {
String paramName = (String) paramNames.nextElement();
log.info(paramName + ": " + request.getHeader(paramName));
}*/
String ip = request.getHeader("X-Forwarded-For");
if (isEmptyIp(ip)) {
ip = request.getHeader("Proxy-Client-IP");
if (isEmptyIp(ip)) {
ip = request.getHeader("WL-Proxy-Client-IP");
if (isEmptyIp(ip)) {
ip = request.getHeader("HTTP_CLIENT_IP");
if (isEmptyIp(ip)) {
ip = request.getHeader("X-Real-IP");
if (isEmptyIp(ip)) {
ip = request.getHeader("HTTP_X_FORWARDED_FOR");
if (isEmptyIp(ip)) {
ip = request.getRemoteAddr();
if (LOCALHOST_STR.equals(ip) || LOCALHOST_IP_STR.equals(ip)) {
// 根据网卡取本机配置的IP
ip = getLocalAddr();
}
}
}
}
}
}
} else if (ip.length() > IP_LEN) {
String[] ips = ip.split(",");
for (int index = 0; index < ips.length; index++) {
String strIp = ips[index];
if (!isEmptyIp(ip)) {
ip = strIp;
break;
}
}
}
return ip;
}
/**
* IP是否为空
*
* @param ip
* @return
*/
private static boolean isEmptyIp(String ip) {
if (StrUtil.isEmpty(ip) || UNKNOWN_STR.equalsIgnoreCase(ip)) {
return true;
}
return false;
}
/**
* 获取本机的IP地址
*/
public static String getLocalAddr() {
try {
return InetAddress.getLocalHost().getHostAddress();
} catch (UnknownHostException e) {
log.error("InetAddress.getLocalHost() is Error", e);
}
return "";
}
/**
* 获取用户真实IP地址,不直接使用request.getRemoteAddr();的原因是有可能用户使用了代理软件方式避免真实IP地址,
*
* 可是,如果通过了多级反向代理的话,X-Forwarded-For的值并不止一个,而是一串IP值,究竟哪个才是真正的用户端的真实IP呢?
* 答案是取X-Forwarded-For中第一个非unknown的有效IP字符串。
*
* 如:X-Forwarded-For:192.168.1.110, 192.168.1.120, 192.168.1.130,
* 192.168.1.100
*
* 用户真实IP为: 192.168.1.110
*
* @param request
* @return
*/
public static String getIpAddress(ServerHttpRequest request) {
HttpHeaders headers = request.getHeaders();
// log.info(JSONUtil.toJsonStr(headers));
String ipAddress = headers.getFirst("X-ClientIP");
if (StrUtil.isEmpty(ipAddress) || UNKNOWN_STR.equalsIgnoreCase(ipAddress)) {
ipAddress = headers.getFirst("X-Forwarded-For");
}
if (StrUtil.isEmpty(ipAddress) || UNKNOWN_STR.equalsIgnoreCase(ipAddress)) {
ipAddress = headers.getFirst("Proxy-Client-IP");
}
if (StrUtil.isEmpty(ipAddress) || UNKNOWN_STR.equalsIgnoreCase(ipAddress)) {
ipAddress = headers.getFirst("WL-Proxy-Client-IP");
}
if (StrUtil.isEmpty(ipAddress) || UNKNOWN_STR.equalsIgnoreCase(ipAddress)) {
ipAddress = headers.getFirst("HTTP_CLIENT_IP");
}
if (StrUtil.isEmpty(ipAddress) || UNKNOWN_STR.equalsIgnoreCase(ipAddress)) {
ipAddress = headers.getFirst("HTTP_X_FORWARDED_FOR");
}
if (StrUtil.isEmpty(ipAddress) || UNKNOWN_STR.equalsIgnoreCase(ipAddress)) {
ipAddress = headers.getFirst("X-Real-IP");
}
if (StrUtil.isEmpty(ipAddress) || UNKNOWN_STR.equalsIgnoreCase(ipAddress)) {
ipAddress = Optional.ofNullable(request.getRemoteAddress())
.map(address -> address.getAddress().getHostAddress())
.orElse("");
if (LOCALHOST_STR.equals(ipAddress)|| LOCALHOST_IP_STR.equals(ipAddress)) {
// 根据网卡取本机配置的IP
ipAddress = getLocalAddr();
}
}
// 对于通过多个代理的情况,第一个IP为客户端真实IP,多个IP按照','分割
if (ipAddress != null && ipAddress.length() > IP_LEN) {
int index = ipAddress.indexOf(",");
if (index > 0) {
ipAddress = ipAddress.substring(0, index);
}
}
return ipAddress;
}
/**
* 获取客户端端口
* @param request
* @return
*/
public static Integer getPort(ServerHttpRequest request){
return request.getRemoteAddress().getPort();
}
}
参考