操作日志

一、基础准备

(存储操作日志建议不要存储数据库,用户量和访问量大对性能影响很大,使用 ``logback-spring`把日志写进文件已经够用了,日志输出尽量详细点,直接下载日志文件就可以了)

使用的操作记录日志表的 建表SQL

DROP TABLE IF EXISTS `t_operation_log`;
CREATE TABLE `t_operation_log`  (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `operation_user_id` int(11) NULL DEFAULT NULL COMMENT '操作人ID',
  `operation_username` varchar(30) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '操作人名称',
  `operation_module` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '操作模块',
  `operation_events` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '具体操作事件',
  `operation_url` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '操作url',
  `operation_data` varchar(3048) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '操作附带数据',
  `operation_status` tinyint(1) NOT NULL COMMENT '操作是否正常,1正常操作, 0 操作异常',
  `operation_ip` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '操作所在IP',
  `operation_result` varchar(512) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '操作结果',
  `add_time` datetime(0) NOT NULL COMMENT '操作时间',
  `deleted` tinyint(1) NOT NULL DEFAULT 0 COMMENT '1 删除,0 未删除',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 32 CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '操作日志表' ROW_FORMAT = Compact;

SET FOREIGN_KEY_CHECKS = 1;  -- 开启外键约束 --

在项目中编写其实体类

在这个实体类之中,写了私有化的构造函数,不允许 new 来创建对象。

可以 减少了数据冲突和不一致的可能性。 (目前就了解这么多,嘿嘿!)

@Data
@TableName("t_operation_log")
@ApiModel(value = "OperationLog对象", description = "操作日志表")
public class OperationLog implements Serializable,Cloneable {

    private static final long serialVersionUID = 1L;

    /**
     * 实现 Cloneable 克隆拷贝
     * 创建一个 默认 对象,用于作为克隆的源数据
     */
    private static final OperationLog log = new OperationLog();

    /**
     * 获取克隆对象, 避免new的方式创建
     * @return {@link OperationLog}
     */
    public static OperationLog getInstance(){
        try {
            return (OperationLog) log.clone();
        } catch (CloneNotSupportedException e) {
            return new OperationLog();
        }
    }

    /**
     * 重写克隆方法
     * @return {@link OperationLog}
     */
    public OperationLog clone() throws CloneNotSupportedException {
        return (OperationLog) super.clone();
    }
    /**
     * 私有化构造函数,不允许 new
     */
    private OperationLog(){
        this.deleted = false;
    }

    @TableId(type = IdType.AUTO)
    private Integer id;

    @ApiModelProperty("操作人ID")
    private Integer operationUserId;

    @ApiModelProperty("操作人名称")
    private String operationUsername;


    @ApiModelProperty("操作模块")
    private String operationModule;

    @ApiModelProperty("具体操作事件")
    private String operationEvents;

    @ApiModelProperty("操作Url")
    private String operationUrl;

    @ApiModelProperty("操作附带数据")
    private String operationData;

    @ApiModelProperty("操作是否正常,1正常操作, 0 操作异常")
    private Boolean operationStatus;

    @ApiModelProperty("操作结果")
    private String operationResult;

    @ApiModelProperty("操作所在IP")
    private String operationIp;

    @ApiModelProperty("操作时间")
    private LocalDateTime addTime;

    @ApiModelProperty("1 删除,0 未删除")
    private Boolean deleted;


}

二、创建日志注解

这里就简单创建了一个注解,注解的值的部分比较简单,在学习和复习的时候,可以学习 若以框架的注解,里面的内容比较丰富和完善。但是丰富完善的内容就需要在 切面类中,进行一些情况上面的判断。

@Target({ElementType.METHOD}) //注解在方法上
@Retention(RetentionPolicy.RUNTIME) //运行时 ,该注解生效
public @interface OperationLogDesc {

    /**
     * 操作模块
     */
    String module();

    /**
     * 操作事件
     */
    String events();

}

若依框架自定义日志注解

/**
 * 自定义操作日志记录注解
 * 
 * @author ruoyi
 *
 */
@Target({ ElementType.PARAMETER, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Log
{
    /**
     * 模块
     */
    public String title() default "";

    /**
     * 功能
     */
    public BusinessType businessType() default BusinessType.OTHER;

    /**
     * 操作人类别
     */
    public OperatorType operatorType() default OperatorType.MANAGE;

    /**
     * 是否保存请求的参数
     */
    public boolean isSaveRequestData() default true;

    /**
     * 是否保存响应的参数
     */
    public boolean isSaveResponseData() default true;

    /**
     * 排除指定的请求参数
     */
    public String[] excludeParamNames() default {};
}

三、使用注解

	@RequestMapping("/info")
    @OperationLogDesc(module = "测试——学生信息查询" , events = "学生信息-查询")  //!!!!!!!!!!!!!!
    public R info(String id) {
        Students stu = studentService.getinfo(id);
        return R.success(stu);
    }

四、切面实现

因为我们要使用的是 aop 的切面类来实现相关功能,所以,要引入 aop 的依赖

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>

下面就是最最最最最最最重要的部分了!!!!!!!!!

1、代码总览

@Aspect //声明是 aspect 切面类
@Component //声明是 spring 容器中的 bean
@Slf4j  // 日志
public class LoggerAspect {

    @Autowired
    public OperationLogService operationLogService;

    /**
     * FastThreadLocal 依赖于  netty,如果不想用netty,可以使用jdk自带的 ThreadLocal
     */
    final ThreadLocal<OperationLog> logThreadLocal = new ThreadLocal<>();
    final DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH:mm:ss");

    /**
     * 切点
     * 所有 com.*.*.controller 包下的所有方法
     */
    @Pointcut("execution(* com.*.*.controller.*.*(..))") // 切点 所有 com.*.*.controller 包下的所有方法
    public void logPointcut() {
    }


    /**
     * 请求前置通知
     * 切点 执行前
     *
     * @param joinPoint 切点
     */
    @Before("logPointcut()")
    public void beforeLog(JoinPoint joinPoint) throws NoSuchMethodException {

        // 获取请求参数
        //将方法调用时传入的参数 joinPoint 转换为字符串形式
        String params = Arrays.toString(joinPoint.getArgs());

        // 鉴权会话获取 当前登录的用户信息,我用的是 shiro,根据情况改变
//        Subject currentUser = SecurityUtils.getSubject();
//        User user = (User)currentUser.getPrincipal();
//        Integer userId = null;
//        String userName = null;
//        if(User!= null){
//            userId = User.getId();
//            userName = User.getUsername();
//        }else{
//            /*
//              因为登录接口没有登录会话状态,无法获取到用户信息,从 登录请求参数中获取 登录用户名
//              @see 例如:登录请求肯定是 post请求,上方 "params" 参数已经获取到 请求参数信息,只要判断里面是否有用户名信息 验证是否为登录接口,然后字符串截取获取用户名。。这个方法是我能想到最快捷的
//              示例登录接口参数:我的登录 请求json [LoginDTO(username=1001010, password=132456,code='A5C5')]
//             */
//            if(params.contains("username=")){
//                userName = params.substring(params.indexOf("username=") + 9, params.indexOf(", password="));
//                // 登录参数密码 简单脱密一下
//                params = params.replace("password=", "changshayueluqu_"); //将password= 替换为 changshayueluqu_
//            }
//        }
        LocalDateTime now = LocalDateTime.now();
        log.info("--------请求前置日志输出开始--------");
        /**RequestContextHolder 用于获取当前请求的上下文信息
         * getRequestAttributes()方法返回一个RequestAttributes对象,这里通过强制类型转换将其转换为ServletRequestAttributes。
         * ServletRequestAttributes是RequestAttributes的一个实现类,专门用于处理基于 Servlet 的请求上下文。
         */
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        /**
         * 由于getRequestAttributes()方法可能返回null(例如在非 Web 请求的上下文中调用)
         * 这里使用Objects.requireNonNull()方法确保attributes不为null
         * 如果attributes不为null,则调用其getRequest()方法获取HttpServletRequest对象
         * 这个对象包含了与当前 HTTP 请求相关的所有信息。
         */
        HttpServletRequest request = Objects.requireNonNull(attributes).getRequest();
        log.info("请求访问时间: {}", dateTimeFormatter.format(now));
        // 获取请求url
        String requestUrl = request.getRequestURL().toString();
        log.info("请求url: {}", requestUrl);
        // 获取method
        log.info("请求方式: {}", request.getMethod());
        log.info("请求参数列表: {}", params);
        log.info("操作人ID: {}", 33333);


        // 验证请求方法是否带有操作日志注解
        /**
         * joinPoint.getSignature() 获取与当前连接点(被拦截的方法调用)相关的签名信息。
         * MethodSignature 是一个接口,它表示一个方法签名。
         * 获取实际调用的 method 对象,该对象包括了 方法名、参数列表、返回类型等。
         */
        Method method = ((MethodSignature) joinPoint.getSignature()).getMethod();
        /**
         * method.getAnnotation(OperationLogDesc.class) 获取当前方法上带有 OperationLogDesc 注解的实例。
         */
        OperationLogDesc operationLogDesc = method.getAnnotation(OperationLogDesc.class);
        if (operationLogDesc != null) {

            // 操作日志记录
            OperationLog operationLog = OperationLog.getInstance();
            operationLog.setAddTime(now);
            operationLog.setOperationModule(operationLogDesc.module());
            operationLog.setOperationEvents(operationLogDesc.events());
            operationLog.setOperationData(params);
            operationLog.setOperationUrl(requestUrl);
            // 操作人ID
            operationLog.setOperationUserId(666);
            operationLog.setOperationUsername("666");

            // IP地址
            operationLog.setOperationIp(IpUtil.getIpAddr(request));
            logThreadLocal.set(operationLog);
        }
    }

    /**
     * 请求后置通知,请求完成会进入到这个方法
     *
     * @param result 响应结果json
     */
    @AfterReturning(value = "logPointcut()", returning = "result")
    public void afterReturningLogger(Object result) {
        // 程序运时间(毫秒)
        log.info("请求结束时间: {}", dateTimeFormatter.format(LocalDateTime.now()));
        log.info("--------后台管理请求后置日志输出完成--------");
        // 保存操作日志
        OperationLog operationLog = logThreadLocal.get();
        if (operationLog != null) {
            operationLog.setOperationStatus(true);
            // 用的 是 阿里巴巴的 fastjson
            operationLog.setOperationResult(JSONObject.toJSONString(result));
            // 调用具体的 service 保存到数据库中
            operationLogService.save(operationLog);
            // 移除本地线程数据
            logThreadLocal.remove();
        }
    }





    /**
     * 异常通知,请求异常会进入到这个方法
     */
    @AfterThrowing(value = "logPointcut()", throwing = "throwable")
    public void throwingLogger(Throwable throwable) {

        log.error("ErrorMessage:请根据异常产生时间前往异常日志查看相关信息");
        log.error("--------后台管理请求异常日志输出完成--------");
        // 保存操作日志
        OperationLog operationLog = logThreadLocal.get();
        if (operationLog != null) {
            operationLog.setOperationStatus(false);
            String throwableStr = throwable.toString();
            if(throwableStr.contains(":")){
                throwableStr = throwableStr.substring(throwableStr.indexOf(":") + 1);
            }
            operationLog.setOperationResult(throwableStr);
            // 调用具体的 service 保存到数据库中
            operationLogService.save(operationLog);
            // 移除本地线程数据
            logThreadLocal.remove();
        }

    }


}

2、理解解析

首先就是定义一个类,表明他是切面类,加入到 spring 容器之中,加上日志注解,可以提供看到日志信息

@Aspect //声明是 aspect 切面类
@Component //声明是 spring 容器中的 bean
@Slf4j  // 日志
public class LoggerAspect {

}

插入到数据库的操作,因为我使用的是 mybatisplus,也可以直接引用 自带的方法插入数据库,可以直接引入 mapper 层

    @Autowired
    public OperationLogService operationLogService;

使用 ThreadLocal<>() 来存储 OperationLog 对象,保证 日志对象的独立性

   /**
     * FastThreadLocal 依赖于  netty,如果不想用netty,可以使用jdk自带的 ThreadLocal
     */
    final ThreadLocal<OperationLog> logThreadLocal = new ThreadLocal<>();

创建了一个时间模板对象

    final DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH:mm:ss");

定义切点,这里是所有的controller中的方法

    /**
     * 切点
     * 所有 com.*.*.controller 包下的所有方法
     */
    @Pointcut("execution(* com.*.*.controller.*.*(..))") // 切点 所有 com.*.*.controller 包下的所有方法
    public void logPointcut() {
    }

前置通知

请求的前置通知,就是在方法执行前,起作用

JoinPoint 就指代目标方法执行过程中的连接点,它提供了一系列方法来获取与当前被拦截方法相关的信息

 /**
     * 请求前置通知
     * 切点 执行前
     *
     * @param joinPoint 切点
     */
    @Before("logPointcut()")
    public void beforeLog(JoinPoint joinPoint) throws NoSuchMethodException {

    }

利用 joinPoint.getArgs() 获取得到目标方法的参数,并将其转换为字符串数组

        // 获取请求参数
        //将方法调用时传入的参数 joinPoint 转换为字符串形式
        String params = Arrays.toString(joinPoint.getArgs());

获取当前登录用户信息的方法,也判断了未登录时的状况

获取当前时间

        // 鉴权会话获取 当前登录的用户信息,我用的是 shiro,根据情况改变
//        Subject currentUser = SecurityUtils.getSubject();
//        User user = (User)currentUser.getPrincipal();
//        Integer userId = null;
//        String userName = null;
//        if(User!= null){
//            userId = User.getId();
//            userName = User.getUsername();
//        }else{
//            /*
//              因为登录接口没有登录会话状态,无法获取到用户信息,从 登录请求参数中获取 登录用户名
//              @see 例如:登录请求肯定是 post请求,上方 "params" 参数已经获取到 请求参数信息,只要判断里面是否有用户名信息 验证是否为登录接口,然后字符串截取获取用户名。。这个方法是我能想到最快捷的
//              示例登录接口参数:我的登录 请求json [LoginDTO(username=1001010, password=132456,code='A5C5')]
//             */
//            if(params.contains("username=")){
//                userName = params.substring(params.indexOf("username=") + 9, params.indexOf(", password="));
//                // 登录参数密码 简单脱密一下
//                params = params.replace("password=", "changshayueluqu_"); //将password= 替换为 changshayueluqu_
//            }
//        }
        LocalDateTime now = LocalDateTime.now();
        log.info("--------请求前置日志输出开始--------");

获取 HTTP 请求的方法 (自己看看吧)

然后操作人id ,因为没有登录相关,自己给了一个

/**
 * RequestContextHolder 用于获取当前请求的上下文信息
 * getRequestAttributes()方法返回一个RequestAttributes对象,这里通过强制类型转换将其转换为ServletRequestAttributes。
 * ServletRequestAttributes是RequestAttributes的一个实现类,专门用于处理基于 Servlet 的请求上下文。
 */
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
 /**
  * 由于getRequestAttributes()方法可能返回null(例如在非 Web 请求的上下文中调用)
  * 这里使用Objects.requireNonNull()方法确保attributes不为null
  * 如果attributes不为null,则调用其getRequest()方法获取HttpServletRequest对象
  * 这个对象包含了与当前 HTTP 请求相关的所有信息。
  */
HttpServletRequest request = Objects.requireNonNull(attributes).getRequest();
log.info("请求访问时间: {}", dateTimeFormatter.format(now));
// 获取请求url
String requestUrl = request.getRequestURL().toString();
log.info("请求url: {}", requestUrl);
// 获取method
log.info("请求方式: {}", request.getMethod());
log.info("请求参数列表: {}", params);
log.info("操作人ID: {}", 33333);

首先就是通过 joinPoint.getSignature() 得到方法签名信息 然后得到方法,再通过方法获取 注解,看看注解有无 自定义注解 OperationLogDesc

有的话,就进行操作信息的记录 ( IpUtil 是一个工具类,在本文最后的备注里

并放进线程中 !!!!!!!!!

		// 验证请求方法是否带有操作日志注解
        /**
         * joinPoint.getSignature() 获取与当前连接点(被拦截的方法调用)相关的签名信息。
         * MethodSignature 是一个接口,它表示一个方法签名。
         * 获取实际调用的 method 对象,该对象包括了 方法名、参数列表、返回类型等。
         */
        Method method = ((MethodSignature) joinPoint.getSignature()).getMethod();
        /**
         * method.getAnnotation(OperationLogDesc.class) 获取当前方法上带有 OperationLogDesc 注解的实例。
         */
        OperationLogDesc operationLogDesc = method.getAnnotation(OperationLogDesc.class);
        if (operationLogDesc != null) {

            // 操作日志记录
            OperationLog operationLog = OperationLog.getInstance();
            operationLog.setAddTime(now);
            operationLog.setOperationModule(operationLogDesc.module());
            operationLog.setOperationEvents(operationLogDesc.events());
            operationLog.setOperationData(params);
            operationLog.setOperationUrl(requestUrl);
            // 操作人ID
            operationLog.setOperationUserId(666);
            operationLog.setOperationUsername("666");

            // IP地址
            operationLog.setOperationIp(IpUtil.getIpAddr(request));
            logThreadLocal.set(operationLog);
        }

后置通知

请求方法执行后,执行此方法

    /**
     * 请求后置通知,请求完成会进入到这个方法
     *
     * @param result 响应结果json
     */
    @AfterReturning(value = "logPointcut()", returning = "result")
    public void afterReturningLogger(Object result) {
        
    }

记录一个请求的结束时间

通过线程得到 operationLog 对象 保证对象的唯一性和统一 !!!!!

记录操作正常

操作结果 将 JSON 转换成字符串 (方便看)

保存到数据库之中

最后移除线程

// 程序运时间(毫秒)
log.info("请求结束时间: {}", dateTimeFormatter.format(LocalDateTime.now()));
log.info("--------后台管理请求后置日志输出完成--------");
// 保存操作日志
OperationLog operationLog = logThreadLocal.get();
if (operationLog != null) {
    operationLog.setOperationStatus(true);
    // 用的 是 阿里巴巴的 fastjson
    operationLog.setOperationResult(JSONObject.toJSONString(result));
    // 调用具体的 service 保存到数据库中
    operationLogService.save(operationLog);
    // 移除本地线程数据
    logThreadLocal.remove();
}

异常通知

请求异常就会进入这个方法

    /**
     * 异常通知,请求异常会进入到这个方法
     */
    @AfterThrowing(value = "logPointcut()", throwing = "throwable")
    public void throwingLogger(Throwable throwable) {
        
    }

通过先程得到 OperationLog 对象 保证唯一性

给定 失败的 (false)状态

处理异常信息,如果异常信息包括 :(冒号),只截取异常信息,不要异常类型

存数据库,移除线程

        log.error("ErrorMessage:请根据异常产生时间前往异常日志查看相关信息");
        log.error("--------后台管理请求异常日志输出完成--------");
        // 保存操作日志
        OperationLog operationLog = logThreadLocal.get();
        if (operationLog != null) {
            operationLog.setOperationStatus(false);
            String throwableStr = throwable.toString();
            if(throwableStr.contains(":")){
                throwableStr = throwableStr.substring(throwableStr.indexOf(":") + 1);
            }
            operationLog.setOperationResult(throwableStr);
            // 调用具体的 service 保存到数据库中
            operationLogService.save(operationLog);
            // 移除本地线程数据
            logThreadLocal.remove();
        }

备注

所需依赖

 		<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.31</version>
        </dependency>

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.2.16</version>
        </dependency>

        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.3.1.tmp</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>

        <dependency>
            <groupId>io.swagger</groupId>
            <artifactId>swagger-annotations</artifactId>
            <version>1.6.6</version>
        </dependency>

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.83_noneautotype</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>

IpUtil

public class IpUtil {
    private static final Logger log = LoggerFactory.getLogger(IpUtil.class);

    private IpUtil() {
    }

    public static String getIpAddr(HttpServletRequest request) {
        String ipAddress;
        try {
            ipAddress = request.getHeader("x-forwarded-for");
            if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
                ipAddress = request.getHeader("Proxy-Client-IP");
            }

            if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
                ipAddress = request.getHeader("WL-Proxy-Client-IP");
            }

            if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
                ipAddress = request.getRemoteAddr();
                if ("127.0.0.1".equals(ipAddress)) {
                    InetAddress inet = null;

                    try {
                        inet = InetAddress.getLocalHost();
                    } catch (UnknownHostException var4) {
                        log.error(var4.getMessage(), var4);
                    }

                    ipAddress = ((InetAddress)Objects.requireNonNull(inet)).getHostAddress();
                }
            }

            if (ipAddress != null && ipAddress.length() > 15 && ipAddress.indexOf(",") > 0) {
                ipAddress = ipAddress.substring(0, ipAddress.indexOf(","));
            }
        } catch (Exception var5) {
            ipAddress = "";
        }

        return ipAddress;
    }

    public static String getIpAddr() {
        return getV4OrV6IP();
    }

    public static String getV4OrV6IP() {
        String ip = null;
        String test = "http://test.ipw.cn";
        StringBuilder inputLine = new StringBuilder();
        BufferedReader in = null;

        try {
            URL url = new URL(test);
            HttpURLConnection urlConnection = (HttpURLConnection)url.openConnection();
            in = new BufferedReader(new InputStreamReader(urlConnection.getInputStream(), StandardCharsets.UTF_8));

            String read;
            while((read = in.readLine()) != null) {
                inputLine.append(read);
            }

            ip = inputLine.toString();
        } catch (Exception var16) {
            log.error("获取网络IP地址异常,这是具体原因: ", var16);
        } finally {
            if (in != null) {
                try {
                    in.close();
                } catch (IOException var15) {
                    var15.printStackTrace();
                }
            }

        }

        if (ip == null) {
            ip = "127.0.0.1";
            log.info("获取网络IP地址异常, 赋值默认ip: 【{}】", ip);
        }

        return ip;
    }
}

ip问题

数据库记录为 0:0:0:0:0:0:0:1

原因
0:0:0:0:0:0:0:1是属于ipv6,但是本机又没有设置ipv6,后来我又进行另一台电脑做测试,发现这种情况只有在服务器和客户端都在同一台电脑上才会出现(例如用localhost访问的时候才会出现),原来是hosts配置文件的问题 windows的hosts文件路径:C:\Windows\System32\drivers\etc\hosts linux的host路径:/etc/hosts

解决措施
注释掉文件中的 # ::1 localhost 这一行即可解决问题。不过不起作用。 最有效的方式就是改变请求的ip,不要使用localhost:8080 使用127.0.0.1:8080或者ip:8080。百分百管用

Posted on   放纵日放纵  阅读(40)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
点击右上角即可分享
微信分享提示