27 用户ip与客户端ip获取
package com.utils;
import org.apache.commons.lang3.StringUtils;
import javax.servlet.http.HttpServletRequest;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.util.Enumeration;
/**
* @Description IP获取工具
*/
public class IpUtils {
/**
* 获取用户真实IP地址,不使用request.getRemoteAddr();的原因是有可能用户使用了代理软件方式避免真实IP地址,
* <p>
* 可是,如果通过了多级反向代理的话,X-Forwarded-For的值并不止一个,而是一串IP值,究竟哪个才是真正的用户端的真实IP呢?
* 答案是取X-Forwarded-For中第一个非unknown的有效IP字符串。
* <p>
* 如:X-Forwarded-For:192.168.1.110, 192.168.1.120, 192.168.1.130,
* 192.168.1.100
* <p>
* 用户真实IP为: 192.168.1.110
*
* @param request
* @return
*/
public static String getIpAddress(HttpServletRequest request) {
String Xip = request.getHeader("X-Real-IP");
String XFor = request.getHeader("X-Forwarded-For");
if(StringUtils.isNotEmpty(XFor) && !"unKnown".equalsIgnoreCase(XFor)){
//多次反向代理后会有多个ip值,第一个ip才是真实ip
int index = XFor.indexOf(",");
if(index != -1){
return XFor.substring(0,index);
}else{
return XFor;
}
}
XFor = Xip;
if(StringUtils.isNotEmpty(XFor) && !"unKnown".equalsIgnoreCase(XFor)){
return XFor;
}
if (StringUtils.isBlank(XFor) || "unknown".equalsIgnoreCase(XFor)) {
XFor = request.getHeader("Proxy-Client-IP");
}
if (StringUtils.isBlank(XFor) || "unknown".equalsIgnoreCase(XFor)) {
XFor = request.getHeader("WL-Proxy-Client-IP");
}
if (StringUtils.isBlank(XFor) || "unknown".equalsIgnoreCase(XFor)) {
XFor = request.getHeader("HTTP_CLIENT_IP");
}
if (StringUtils.isBlank(XFor) || "unknown".equalsIgnoreCase(XFor)) {
XFor = request.getHeader("HTTP_X_FORWARDED_FOR");
}
if (StringUtils.isBlank(XFor) || "unknown".equalsIgnoreCase(XFor)) {
XFor = request.getRemoteAddr();
}
return XFor;
}
/**
* 获取服务器IP地址
* @return
*/
@SuppressWarnings("unchecked")
public static String getServerIp(){
String SERVER_IP = "";
try {
Enumeration netInterfaces = NetworkInterface.getNetworkInterfaces();
InetAddress ip = null;
while (netInterfaces.hasMoreElements()) {
NetworkInterface ni = (NetworkInterface) netInterfaces.nextElement();
ip = (InetAddress) ni.getInetAddresses().nextElement();
SERVER_IP = ip.getHostAddress();
if (!ip.isSiteLocalAddress() && !ip.isLoopbackAddress()
&& ip.getHostAddress().indexOf(":") == -1) {
SERVER_IP = ip.getHostAddress();
break;
} else {
ip = null;
}
}
} catch (SocketException e) {
e.printStackTrace();
}
return SERVER_IP;
}
}
/**
* 获取服务器IP地址
* @return
*/
public static String getServerIp(){
return System.getProperty("ServiceIP");
}
28 Jedis配置
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import redis.clients.jedis.HostAndPort;
import redis.clients.jedis.JedisCluster;
import redis.clients.jedis.JedisPoolConfig;
import java.util.HashSet;
import java.util.Set;
@Configuration
@ConditionalOnClass({JedisCluster.class})
public class RedisConfig {
@Value("${spring.redis.cluster.nodes}")
private String clusterNodes;
@Value("${spring.redis.cluster.closeWindowNodes}")
private String closeWindowNodes;
@Value("${spring.redis.timeout}")
private int timeout;
@Value("${spring.redis.jedis.pool.max-active}")
private int maxActive;
@Value("${spring.redis.jedis.pool.max-idle}")
private int maxIdle;
@Value("${spring.redis.jedis.pool.min-idle}")
private int minIdle;
@Value("${spring.redis.jedis.pool.max-wait}")
private long maxWaitMillis;
@Value("${spring.redis.maxAttempts}")
private int maxAttempts;
@Value("${spring.redis.closeWindowPassword}")
private String password;
@Bean
@Primary
public JedisCluster getJedisCluster() {
Set<HostAndPort> nodes = new HashSet<>();
// 分割出集群节点
String[] cNodes = clusterNodes.split(",");
for (String node : cNodes) {
String[] hp = node.split(":");
nodes.add(new HostAndPort(hp[0], Integer.parseInt(hp[1])));
}
JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
jedisPoolConfig.setMaxTotal(maxActive);
jedisPoolConfig.setMaxIdle(maxIdle);
jedisPoolConfig.setMinIdle(minIdle);
jedisPoolConfig.setMaxWaitMillis(maxWaitMillis);
// 创建集群对象
JedisCluster jedisCluster = new JedisCluster(nodes, timeout, maxAttempts, jedisPoolConfig);
return jedisCluster;
}
/**
* 关闭弹窗专用redis集群
*/
@Bean(name = "CWJedisCluster")
public JedisCluster getCWJedisCluster() {
Set<HostAndPort> nodes = new HashSet<>();
// 分割出集群节点
String[] cNodes = closeWindowNodes.split(",");
for (String node : cNodes) {
String[] hp = node.split(":");
nodes.add(new HostAndPort(hp[0], Integer.parseInt(hp[1])));
}
JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
jedisPoolConfig.setMaxTotal(maxActive);
jedisPoolConfig.setMaxIdle(maxIdle);
jedisPoolConfig.setMinIdle(minIdle);
jedisPoolConfig.setMaxWaitMillis(maxWaitMillis);
// 创建集群对象
JedisCluster jedisCluster = new JedisCluster(nodes, timeout, timeout, maxAttempts, password, jedisPoolConfig);
return jedisCluster;
}
}
29 线程串联统一uuid设置filter
package com.filter;
import com.utils.CookiesUtil;
import com.utils.IpUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import javax.servlet.*;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.UUID;
@Slf4j
@Order(0)
@Component
public class ThreadNameFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) {
log.info("初始化tomcat线程名过滤器");
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
String uuid = UUID.randomUUID().toString();
Thread.currentThread().setName(uuid);
long startTime = System.currentTimeMillis();
String ipAddress = IpUtils.getIpAddress(request);
log.info("================================用户ip={},UUID={},请求服务端地址开始=【{}】",ipAddress,uuid,request.getRequestURI());
if (request != null){
String cookie1 = request.getHeader("cookie");
Cookie[] cookies = request.getCookies();
StringBuffer cookieStr = new StringBuffer();
if (cookies != null){
for (Cookie cookie : cookies) {
cookieStr.append(cookie.getName()).append("=").append(cookie.getValue()).append(";");
}
}
String userMobile = CookiesUtil.getCookiseValue("u_account", request);
log.info("UUID={},从cookie中获取userMobile:【{}】的请求request.getHeader('cookie')值:{}", uuid,userMobile,cookie1);
log.info("UUID={},从cookie中获取userMobile:【{}】的请求request.getCookies()值:{}", uuid,userMobile,cookieStr);
}
log.info("UUID={},获取请求参数reqdata=【{}】",uuid,request.getParameter("reqdata"));
filterChain.doFilter(servletRequest, servletResponse);
log.info("================================用户ip={},UUID={},请求服务端地址结束=【{}】,耗时=【{}】毫秒",ipAddress,uuid,request.getRequestURI(),System.currentTimeMillis() - startTime);
}
@Override
public void destroy() {
log.info("销毁tomcat线程名过滤器");
}
}
30 日志无侵入脱敏
日志格式;目前仅支持(:)和等号(=)
log.info("your email:{}, your phone:{}", "123456789@qq.com","15310763497");
log.info("your email={}, your cellphone={}", "123456789@qq.com","15310763497");
1.<dependency>
<groupId>pers.liuchengyin</groupId>
<artifactId>logback-desensitization</artifactId>
<version>1.0.0</version>
</dependency>
2.日志打印方式都只需要替换成脱敏的类即可
例如:// 原类
ch.qos.logback.core.rolling.RollingFileAppender
// 替换类
pers.liuchengyin.logbackadvice.LcyRollingFileAppender
3.添加脱敏配置文件(logback-desensitize.yml)该配置文件应该放在resources文件下
# 日志脱敏
log-desensitize:
# 是否忽略大小写匹配,默认为true
ignore: true
# 是否开启脱敏,默认为false
open: true
# pattern下的key/value为固定脱敏规则
pattern:
# 邮箱 - @前第4-7位脱敏
email: "@>(4,7)"
# qq邮箱 - @后1-3位脱敏
qqemail: "@<(1,3)"
# 姓名 - 姓脱敏,如*杰伦
name: 1,1
# 密码 - 所有需要完全脱敏的都可以使用内置的password
password: password
patterns:
# 身份证号,key后面的字段都可以匹配以下规则(用逗号分隔)
- key: identity,idcard
# 定义规则的标识
custom:
# defaultRegex表示使用组件内置的规则:identity表示身份证号 - 内置的18/15位
- defaultRegex: identity
position: 9,13
# 内置的other表示如果其他规则都无法匹配到,则按该规则处理
- defaultRegex: other
position: 9,10
# 电话号码,key后面的字段都可以匹配以下规则(用逗号分隔)
- key: phone,cellphone,mobile
custom:
# 手机号 - 内置的11位手机匹配规则
- defaultRegex: phone
position: 4,7
# 自定义正则匹配表达式:座机号(带区号,号码七位|八位)
- customRegex: "^0[0-9]{2,3}-[0-9]{7,8}"
# -后面的1-4位脱敏
position: "-<(1,4)"
# 自定义正则匹配表达式:座机号(不带区号)
- customRegex: "^[0-9]{7,8}"
position: 3,5
# 内置的other表示如果其他规则都无法匹配到,则按该规则处理
- defaultRegex: other
position: 1,3
# 这种方式不太推荐 - 一旦匹配不上,就不会脱敏
- key: localMobile
custom:
customRegex: "^0[0-9]{2,3}-[0-9]{7,8}"
position: 1,3