当前位置: 首页 > news >正文

MyBatis 插件 + 注解轻松实现数据脱敏

问题

在项目中需要对用户敏感数据进行脱敏处理,例如身份号、手机号等信息进行加密再入库。

解决思路

  • 就是:一种最简单直接的方式,在所有涉及数据敏感的查询到对插入时进行密码加解密

  • 方法二:有方法一到出现对所有重大问题的影响,需要考虑到问题的出现,并且需要考虑可能出现的组员时添加数据的方法。

最后决定采用mybatis的插件在mybatis的SQL执行和结果填充操作上进行切入。上层业务调用不再需要考虑数据的加敏同时也保证了数据的加解密

Mybatis 插件原理

Mybatis 的是通过拦截器实现的,Mabatis 支持对当事人进行拦截

实现

  • 设置对参数中带有敏感参数字段的数据时进行加密

  • 对返回的结果进行解密处理

根据不同的要求,我们只需要对ParameterHandlerResultSetHandler进行切入。定义特定注解,在切入时需要检查字段中是否包含注解来是否加解密。

另外,如果你近期准备面试跳槽,建议在Java面试库小程序在线刷题,涵盖 2000+ 道 Java 面试题,几乎覆盖了所有主流技术面试题。

加注解

定义SensitiveData注解

import java.lang.annotation.*;
/*** 该注解定义在类上* 插件通过扫描类对象是否包含这个注解来决定是否继续扫描其中的字段注解* 这个注解要配合EncryptTransaction注解**/
@Inherited
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface SensitiveData {}

定义EncryptTransaction注解

import java.lang.annotation.*;
/*** 该注解有两种使用方式* ①:配合@SensitiveData加在类中的字段上* ②:直接在Mapper中的方法参数上使用**/
@Documented
@Inherited
@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
public @interface EncryptTransaction {}

加解密工具类

解密

package sicnu.cs.ich.common.interceptor.transaction.service;public interface IDecryptUtil {/*** 解密** @param result resultType的实例* @return T* @throws IllegalAccessException 字段不可访问异常*/<T> T decrypt(T result) throws IllegalAccessException;
}

加密接口

package sicnu.cs.ich.common.interceptor.transaction.service;import java.lang.reflect.Field;public interface IEncryptUtil {/*** 加密** @param declaredFields 加密字段* @param paramsObject   对象* @param <T>            入参类型* @return 返回加密* @throws IllegalAccessException 不可访问*/<T> T encrypt(Field[] declaredFields, T paramsObject) throws IllegalAccessException;
}
package sicnu.cs.ich.common.interceptor.transaction.service.impl;
import org.springframework.stereotype.Component;
import sicnu.cs.ich.api.common.annotations.transaction.EncryptTransaction;
import sicnu.cs.ich.common.interceptor.transaction.service.IDecryptUtil;
import sicnu.cs.ich.common.util.keyCryptor.DBAESUtil;import java.lang.reflect.Field;
import java.util.Objects;@Component
public class DecryptImpl implements IDecryptUtil {/*** 解密** @param result resultType的实例*/@Overridepublic <T> T decrypt(T result) throws IllegalAccessException {//取出resultType的类Class<?> resultClass = result.getClass();Field[] declaredFields = resultClass.getDeclaredFields();for (Field field : declaredFields) {//取出所有被DecryptTransaction注解的字段EncryptTransaction encryptTransaction = field.getAnnotation(EncryptTransaction.class);if (!Objects.isNull(encryptTransaction)) {field.setAccessible(true);Object object = field.get(result);//String的解密if (object instanceof String) {String value = (String) object;//对注解的字段进行逐一解密try {field.set(result, DBAESUtil.decrypt(value));} catch (Exception e) {e.printStackTrace();}}}}return result;}
}

加密实现类

package sicnu.cs.ich.common.interceptor.transaction.service.impl;import com.fasterxml.jackson.databind.ObjectReader;
import org.springframework.stereotype.Component;
import sicnu.cs.ich.api.common.annotations.transaction.EncryptTransaction;
import sicnu.cs.ich.common.interceptor.transaction.service.IEncryptUtil;
import sicnu.cs.ich.common.util.keyCryptor.DBAESUtil;import java.io.ObjectInputStream;
import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.Objects;
import java.util.Random;@Component
public class EncryptUtilImpl implements IEncryptUtil {@Overridepublic <T> T encrypt(Field[] declaredFields, T paramsObject) throws IllegalAccessException {//取出所有被EncryptTransaction注解的字段for (Field field : declaredFields) {EncryptTransaction encryptTransaction = field.getAnnotation(EncryptTransaction.class);if (!Objects.isNull(encryptTransaction)) {field.setAccessible(true);Object object = field.get(paramsObject);//暂时只实现String类型的加密if (object instanceof String) {String value = (String) object;//加密try {field.set(paramsObject, DBAESUtil.encrypt(value));} catch (Exception e) {e.printStackTrace();}}}}return paramsObject;}
}

模拟类

package sicnu.cs.ich.common.interceptor.transaction.service.impl;import org.springframework.stereotype.Component;
import sicnu.cs.ich.api.common.annotations.transaction.EncryptTransaction;
import sicnu.cs.ich.common.interceptor.transaction.service.IDecryptUtil;
import sicnu.cs.ich.common.util.keyCryptor.DBAESUtil;import java.lang.reflect.Field;
import java.util.Objects;@Component
public class DecryptImpl implements IDecryptUtil {/*** 解密** @param result resultType的实例*/@Overridepublic <T> T decrypt(T result) throws IllegalAccessException {//取出resultType的类Class<?> resultClass = result.getClass();Field[] declaredFields = resultClass.getDeclaredFields();for (Field field : declaredFields) {//取出所有被DecryptTransaction注解的字段EncryptTransaction encryptTransaction = field.getAnnotation(EncryptTransaction.class);if (!Objects.isNull(encryptTransaction)) {field.setAccessible(true);Object object = field.get(result);//String的解密if (object instanceof String) {String value = (String) object;//对注解的字段进行逐一解密try {field.set(result, DBAESUtil.decrypt(value));} catch (Exception e) {e.printStackTrace();}}}}return result;}
}

加解密工具类

package sicnu.cs.ich.common.util.keyCryptor;import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.util.Base64;public class DBAESUtil {private static final String DEFAULT_V = "6859505890402435";// 自己填写private static final String KEY = "***";private static final String ALGORITHM = "AES";private static SecretKeySpec getKey() {byte[] arrBTmp = DBAESUtil.KEY.getBytes();// 创建一个空的16位字节数组(默认值为0)byte[] arrB = new byte[16];for (int i = 0; i < arrBTmp.length && i < arrB.length; i++) {arrB[i] = arrBTmp[i];}return new SecretKeySpec(arrB, ALGORITHM);}/*** 加密*/public static String encrypt(String content) throws Exception {final Base64.Encoder encoder = Base64.getEncoder();SecretKeySpec keySpec = getKey();Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");IvParameterSpec iv = new IvParameterSpec(DEFAULT_V.getBytes());cipher.init(Cipher.ENCRYPT_MODE, keySpec, iv);byte[] encrypted = cipher.doFinal(content.getBytes());return encoder.encodeToString(encrypted);}/*** 解密*/public static String decrypt(String content) throws Exception {final Base64.Decoder decoder = Base64.getDecoder();SecretKeySpec keySpec = getKey();Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");IvParameterSpec iv = new IvParameterSpec(DEFAULT_V.getBytes());cipher.init(Cipher.DECRYPT_MODE, keySpec, iv);byte[] base64 = decoder.decode(content);byte[] original = cipher.doFinal(base64);return new String(original);}}

插件实现

参数插件ParameterInterceptor

切入mybatis设置参数时对敏感数据进行加密

Mybatis插件的使用就是通过实现Mybatis中的Interceptor接口

@Intercepts注解

// 使用mybatis插件时需要定义签名
// type标识需要切入的Handler
// method表示要要切入的方法
@Intercepts({
@Signature(type = ParameterHandler.class, method = “setParameters”, args = PreparedStatement.class),
})
package sicnu.cs.ich.common.interceptor.transaction;import com.baomidou.mybatisplus.core.MybatisParameterHandler;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.executor.parameter.ParameterHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import sicnu.cs.ich.api.common.annotations.transaction.EncryptTransaction;
import sicnu.cs.ich.api.common.annotations.transaction.SensitiveData;
import sicnu.cs.ich.common.interceptor.transaction.service.IEncryptUtil;
import sicnu.cs.ich.common.util.keyCryptor.DBAESUtil;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.sql.PreparedStatement;
import java.util.*;@Slf4j
// 注入Spring
@Component
@Intercepts({@Signature(type = ParameterHandler.class, method = "setParameters", args = PreparedStatement.class),
})
public class ParameterInterceptor implements Interceptor {@Autowiredprivate IEncryptUtil IEncryptUtil;@Overridepublic Object intercept(Invocation invocation) throws Throwable {//@Signature 指定了 type= parameterHandler 后,这里的 invocation.getTarget() 便是parameterHandler//若指定ResultSetHandler ,这里则能强转为ResultSetHandlerMybatisParameterHandler parameterHandler = (MybatisParameterHandler) invocation.getTarget();// 获取参数对像,即 mapper 中 paramsType 的实例Field parameterField = parameterHandler.getClass().getDeclaredField("parameterObject");parameterField.setAccessible(true);//取出实例Object parameterObject = parameterField.get(parameterHandler);// 搜索该方法中是否有需要加密的普通字段List<String> paramNames = searchParamAnnotation(parameterHandler);if (parameterObject != null) {Class<?> parameterObjectClass = parameterObject.getClass();//对类字段进行加密//校验该实例的类是否被@SensitiveData所注解SensitiveData sensitiveData = AnnotationUtils.findAnnotation(parameterObjectClass, SensitiveData.class);if (Objects.nonNull(sensitiveData)) {//取出当前当前类所有字段,传入加密方法Field[] declaredFields = parameterObjectClass.getDeclaredFields();IEncryptUtil.encrypt(declaredFields, parameterObject);}// 对普通字段进行加密if (!CollectionUtils.isEmpty(paramNames)) {// 反射获取 BoundSql 对象,此对象包含生成的sql和sql的参数map映射Field boundSqlField = parameterHandler.getClass().getDeclaredField("boundSql");boundSqlField.setAccessible(true);BoundSql boundSql = (BoundSql) boundSqlField.get(parameterHandler);PreparedStatement ps = (PreparedStatement) invocation.getArgs()[0];// 改写参数processParam(parameterObject, paramNames);// 改写的参数设置到原parameterHandler对象parameterField.set(parameterHandler, parameterObject);parameterHandler.setParameters(ps);}}return invocation.proceed();}private void processParam(Object parameterObject, List<String> params) throws Exception {// 处理参数对象  如果是 map 且map的key 中没有 tenantId,添加到参数map中// 如果参数是bean,反射设置值if (parameterObject instanceof Map) {@SuppressWarnings("unchecked")Map<String, String> map = ((Map<String, String>) parameterObject);for (String param : params) {String value = map.get(param);map.put(param, value==null?null:DBAESUtil.encrypt(value));}
//            parameterObject = map;}}private List<String> searchParamAnnotation(ParameterHandler parameterHandler) throws NoSuchFieldException, ClassNotFoundException, IllegalAccessException {Class<MybatisParameterHandler> handlerClass = MybatisParameterHandler.class;Field mappedStatementFiled = handlerClass.getDeclaredField("mappedStatement");mappedStatementFiled.setAccessible(true);MappedStatement mappedStatement = (MappedStatement) mappedStatementFiled.get(parameterHandler);String methodName = mappedStatement.getId();Class<?> mapperClass = Class.forName(methodName.substring(0, methodName.lastIndexOf('.')));methodName = methodName.substring(methodName.lastIndexOf('.') + 1);Method[] methods = mapperClass.getDeclaredMethods();Method method = null;for (Method m : methods) {if (m.getName().equals(methodName)) {method = m;break;}}List<String> paramNames = null;if (method != null) {Annotation[][] pa = method.getParameterAnnotations();Parameter[] parameters = method.getParameters();for (int i = 0; i < pa.length; i++) {for (Annotation annotation : pa[i]) {if (annotation instanceof EncryptTransaction) {if (paramNames == null) {paramNames = new ArrayList<>();}paramNames.add(parameters[i].getName());}}}}return paramNames;}@Overridepublic Object plugin(Object target) {return Plugin.wrap(target, this);}@Overridepublic void setProperties(Properties properties) {}
}

返回值插件ResultSetInterceptor

package sicnu.cs.ich.common.interceptor.transaction;import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.executor.resultset.ResultSetHandler;
import org.apache.ibatis.plugin.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import sicnu.cs.ich.api.common.annotations.transaction.SensitiveData;import java.sql.Statement;
import java.util.ArrayList;
import java.util.Objects;
import java.util.Properties;@Slf4j
@Component
@Intercepts({@Signature(type = ResultSetHandler.class, method = "handleResultSets", args = {Statement.class})
})
public class ResultSetInterceptor implements Interceptor {@Autowiredprivate sicnu.cs.ich.common.interceptor.transaction.service.IDecryptUtil IDecryptUtil;@Overridepublic Object intercept(Invocation invocation) throws Throwable {//取出查询的结果Object resultObject = invocation.proceed();if (Objects.isNull(resultObject)) {return null;}//基于selectListif (resultObject instanceof ArrayList) {@SuppressWarnings("unchecked")ArrayList<Objects> resultList = (ArrayList<Objects>) resultObject;if (!CollectionUtils.isEmpty(resultList) && needToDecrypt(resultList.get(0))) {for (Object result : resultList) {//逐一解密IDecryptUtil.decrypt(result);}}//基于selectOne} else {if (needToDecrypt(resultObject)) {IDecryptUtil.decrypt(resultObject);}}return resultObject;}private boolean needToDecrypt(Object object) {Class<?> objectClass = object.getClass();SensitiveData sensitiveData = AnnotationUtils.findAnnotation(objectClass, SensitiveData.class);return Objects.nonNull(sensitiveData);}@Overridepublic Object plugin(Object target) {return Plugin.wrap(target, this);}@Overridepublic void setProperties(Properties properties) {}
}

使用

注意解在实体类上

import lombok.*;
import org.springframework.security.core.userdetails.UserDetails;
import sicnu.cs.ich.api.common.annotations.transaction.EncryptTransaction;
import sicnu.cs.ich.api.common.annotations.transaction.SensitiveData;@With
@Builder
@Data
@NoArgsConstructor
@AllArgsConstructor
@SensitiveData // 插件只对加了该注解的类进行扫描,只有加了这个注解的类才会生效
public class User implements Serializable {private Integer id;private String username;private String openId;private String password;// 表明对该字段进行加密@EncryptTransactionprivate String email;// 表明对该字段进行加密@EncryptTransactionprivate String mobile;private Date createTime;private Date expireTime;private Boolean status = true;
}

注解在参数上

import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import sicnu.cs.ich.api.common.annotations.transaction.EncryptTransaction;@Mapper
public interface UserMapper extends BaseMapper<User> {// 只需要在参数前加上@EncryptTransaction 即可long countByEmail(@EncryptTransaction @Param("email") String email);long countByMobile(@EncryptTransaction @Param("mobile") String mobile);}

相关文章:

MyBatis 插件 + 注解轻松实现数据脱敏

问题在项目中需要对用户敏感数据进行脱敏处理&#xff0c;例如身份号、手机号等信息进行加密再入库。解决思路就是&#xff1a;一种最简单直接的方式&#xff0c;在所有涉及数据敏感的查询到对插入时进行密码加解密方法二&#xff1a;有方法一到出现对所有重大问题的影响&#…...

MySQL优化篇-MySQL压力测试

备注:测试数据库版本为MySQL 8.0 MySQL压力测试概述 为什么压力测试很重要&#xff1f;因为压力测试是唯一方便有效的、可以学习系统在给定的工作负载下会发生什么的方法。压力测试可以观察系统在不同压力下的行为&#xff0c;评估系统的容量&#xff0c;掌握哪些是重要的变化…...

CF43A Football 题解

CF43A Football 题解题目链接字面描述题面翻译题面描述题目描述输入格式输出格式样例 #1样例输入 #1样例输出 #1样例 #2样例输入 #2样例输出 #2代码实现题目 链接 https://www.luogu.com.cn/problem/CF43A 字面描述 题面翻译 题面描述 两只足球队比赛&#xff0c;现给你进…...

Nginx常用命令及具体应用(Linux系统)

目录 一、常用命令 1、查看Nginx版本命令&#xff0c;在sbin目录下 2、检查配置文件的正确性 3、启动和停止Nginx 4、查看日志&#xff0c;在logs目录下输入指令&#xff1a; 5、重新加载配置文件 二、Nginx配置文件结构 三、Nginx具体应用 1、部署静态资源 2、反向代…...

从零实现Web服务器(三):日志优化,压力测试,实战接收HTTP请求,实战响应HTTP请求

文章目录一、日志系统的运行流程1.1 异步日志和同步日志的不同点1.2 缓冲区的实现二、基于Webbench的压力测试三、HTTP请求报文解析http报文处理流程epoll相关代码服务器接收http请求四、HTTP请求报文响应一、日志系统的运行流程 步骤: 单例模式&#xff08;局部静态变量懒汉…...

MFC入门

1.什么是MFC?全称是Microsoft Foundation Class Library&#xff0c;我们称微软基础类库。它封装了windows应用程序的各种API以及相关机制的C类库MFC是一个大的类库MFC是一个应用程序框架MFC类库常用的头文件afx.h-----将各种MFC头文件包含在内afxwin.h-------包含了各种MFC窗…...

1、H5+CSS面试题

1, HTML5中新增了哪些内容&#xff1f;广义上的html5指的是最新一代前端开发技术的总称&#xff0c;包括html5&#xff0c;CSS3&#xff0c;新增的webAPI。Html中新增了header,footer,main,nav等语义化标签&#xff0c;新增了video,audio媒体标签&#xff0c;新增了canvas画布。…...

亚马逊云科技重磅发布《亚马逊云科技汽车行业解决方案》

当今&#xff0c;随着万物智联、云计算等领域的高速发展&#xff0c;创新智能网联汽车和车路协同技术正在成为车企加速发展的关键途径&#xff0c;推动着汽车产品从出行代步工具向着“超级智能移动终端”快速转变。挑战无处不在&#xff0c;如何抢先预判&#xff1f;随着近年来…...

Springboot扩展点之FactoryBean

前言FactoryBean是一个有意思&#xff0c;且非常重要的扩展点&#xff0c;之所以说是有意思&#xff0c;是因为它老是被拿来与另一个名字比较类似的BeanFactory来比较&#xff0c;特别是在面试当中&#xff0c;动不动就问你&#xff1a;你了解Beanfactory和FactoryBean的区别吗…...

新库上线 | CnOpenDataA股上市公司交易所监管措施数据

A股上市公司交易所监管措施数据 一、数据简介 证券市场监管是指证券管理机关运用法律的、经济的以及必要的行政手段&#xff0c;对证券的募集、发行、交易等行为以及证券投资中介机构的行为进行监督与管理。 我国《证券交易所管理办法》第十二条规定&#xff0c;证券交易所应当…...

同步辐射XAFS表征方法的应用场景分析

X射线吸收精细结构XAFS表征方法是一种用于研究物质结构和化学环境的分析技术。XAFS 使用 X 射线照射到物质表面&#xff0c;并观察由此产生的 X 光吸收谱。 ​XAFS 技术通常应用于研究高分子物质、生物分子、纳米结构和其他类型的物质。例如&#xff0c;XAFS 可以用来研究高分子…...

06 antdesign react Anchor 不同页面之间实现锚点

react Anchor 不同页面之间实现锚点一、定义二、使用步骤三、开发流程(一)、组件(二)、页面布局(三)、点击事件(四)、总结说明一、react单页面应用&#xff0c;当前页面的锚点二、react单页面应用&#xff0c;不同页面的锚点思路&#xff1a;锚点只能在当前页面使用&#xff0c…...

mysql调优-内存缓冲池

因本地查询和服务器查询相比服务器慢了很多&#xff0c;同样的数据&#xff0c;同样的sql查询&#xff0c;考虑了是不是链接太多了&#xff0c;自行查询了下&#xff0c;我使用的c3p0的链接池&#xff0c;配置一个小时超时&#xff0c;正常情况下是20多个链接&#xff0c;而mys…...

【LeetCode】每日一题(5)

目录 题目&#xff1a;2341. 数组能形成多少数对 - 力扣&#xff08;Leetcode&#xff09; 题目的接口&#xff1a; 解题思路&#xff1a; 代码&#xff1a; 过啦&#xff01;&#xff01;&#xff01; 写在最后&#xff1a; 题目&#xff1a;2341. 数组能形成多少数对 -…...

输入任意多个整数, 把这些数据保存到文件data.txt中.(按ctrl + z)

#pragma once #include <iostream> #include <fstream> using namespace std; /* 输入任意多个整数, 把这些数据保存到文件data.txt中. 如果在输入的过程中, 输入错误, 则提示用户重新输入. 指导用户输入结束(按ctrl z) [每行最多保存10个整数] */ int main() { …...

Mysql数据库的时间(3)一如何用函数插入时间

暂时用下面四个日期函数插入时间 如:insert into Stu(time) values (now()); Mysql的时间函数描述对应的Mysql的时间类型now()/sysdate()NOW()函数以YYYY-MM-DD HH:MM:SS返回当前的日期时间date/time/dateTime/timeStamp/yearcurDate()/current_date()返回当前的日期YYYY-M…...

关于eval函数(将JSON格式的字符串转换成JSON格式对象)

<!DOCTYPE html> <html> <head> <meta charset"utf-8"> <title>关于eval函数</title> </head> <body> <!--JSON是一种行业内的数据交换格式标准。在JS当中以对象的形式存在…...

2023最强软件测试面试题,精选100 道,内附答案版,冲刺金3银4

精挑细选&#xff0c;整理了100道软件测试面试题&#xff0c;都是非常常见的面试题&#xff0c;篇幅较长&#xff0c;所以只放出了题目&#xff0c;答案在评论区&#xff01; 测试技术面试题 1、什么是兼容性测试&#xff1f;兼容性测试侧重哪些方面&#xff1f; 2、我现在有…...

一文搞懂Docker容器里进程的 pid 是如何申请出来的?

如果大家有过在容器中执行 ps 命令的经验&#xff0c;都会知道在容器中的进程的 pid 一般是比较小的。例如下面我的这个例子。 # ps -ef PID USER TIME COMMAND1 root 0:00 ./demo-ie13 root 0:00 /bin/bash21 root 0:00 ps -ef 不知道大家是否和我一样…...

若依框架如何新增自定义主题风格

若依框架新增主题风格1.实现结果2.实现步骤2.1Settings目录下2.2 variables.scss2.3 sidebar.scss2.4 Logo.vue2.5 Siderbar目录下的index.vue1.实现结果 2.实现步骤 需要改动的文件目录&#xff1a; 2.1Settings目录下 <div class"setting-drawer-block-checbox-it…...

别再瞎猜了!YOLOv8 模型缩放(width_multiple)与通道计算(c1,c2)的完整逻辑

YOLOv8模型通道计算与宽度系数的工程化实践指南 在移动端部署YOLOv8模型时&#xff0c;许多工程师会遇到一个典型困境&#xff1a;明明按照官方文档调整了width_multiple参数&#xff0c;却发现模型要么计算量超出预期&#xff0c;要么精度断崖式下跌。这背后其实隐藏着YOLOv8通…...

程序员巫术:用玩偶诅咒删库的同事

——软件测试从业者的专业反思与健康应对在软件开发的战场上&#xff0c;测试工程师常被视为“质量守门人”&#xff0c;肩负着拦截缺陷、守护产品稳定的重任。然而&#xff0c;当一位愤怒的测试员掏出针线缝制玩偶&#xff0c;试图用“巫术”诅咒那位鲁莽删库的同事时&#xf…...

Mojo调用Python模块性能翻倍?深度剖析混合编程内存管理、GIL绕过与ABI兼容性(附实测基准数据)

第一章&#xff1a;Mojo与Python混合编程案例源码分析Mojo 作为兼具 Python 兼容性与系统级性能的新一代编程语言&#xff0c;其与 Python 的混合编程能力是实际工程落地的关键。以下通过一个典型场景——在 Python 主程序中调用 Mojo 实现的高性能向量加法函数——展开源码级剖…...

别再只盯着IoU了!用Python手把手教你计算语义分割的95% Hausdorff距离(附完整代码)

超越IoU&#xff1a;用Python实战95% Hausdorff距离的医学影像分割评估 当我们在医院看到CT扫描图像上肿瘤边缘被红色轮廓线精准勾勒时&#xff0c;很少有人会思考这背后的算法是如何评估自己分割结果的准确性的。传统指标如IoU&#xff08;交并比&#xff09;和Dice系数固然流…...

LFM2.5-1.2B-Thinking-GGUF前端面试题解析实战:模拟面试与答案生成

LFM2.5-1.2B-Thinking-GGUF前端面试题解析实战&#xff1a;模拟面试与答案生成 1. 开篇&#xff1a;AI如何改变前端面试准备方式 前端开发岗位的竞争日益激烈&#xff0c;技术面试的难度也水涨船高。传统的面试准备方式往往效率低下——求职者要么死记硬背网上的标准答案&…...

基于微信小程序实现马拉松报名系统【附项目源码+论文说明】

基于java和微信小程序实现马拉松报名系统演示【内附项目源码LW说明】摘要 随着信息技术在管理上越来越深入而广泛的应用&#xff0c;管理信息系统的实施在技术上已逐步成熟。本文介绍了马拉松报名系统微信小程序的开发全过程。通过分析马拉松报名系统微信小程序管理的不足&…...

3步搞定B站音频提取:BilibiliDown开源工具的终极指南

3步搞定B站音频提取&#xff1a;BilibiliDown开源工具的终极指南 【免费下载链接】BilibiliDown (GUI-多平台支持) B站 哔哩哔哩 视频下载器。支持稍后再看、收藏夹、UP主视频批量下载|Bilibili Video Downloader &#x1f633; 项目地址: https://gitcode.com/gh_mirrors/bi…...

NaViL-9B效果实测:支持中英文混排表格图像的行列结构识别与内容提取

NaViL-9B效果实测&#xff1a;支持中英文混排表格图像的行列结构识别与内容提取 1. 模型介绍 NaViL-9B是新一代原生多模态大语言模型&#xff0c;专为处理复杂视觉-语言任务设计。与常规视觉模型不同&#xff0c;它不仅能够理解图片内容&#xff0c;还能精准解析表格、文档等…...

Vue中实现动态标签页的切换优化与状态管理

1. 动态标签页的核心需求与实现思路 在后台管理系统这类多页面应用中&#xff0c;动态标签页几乎是标配功能。想象一下你正在使用某电商后台&#xff0c;同时开着商品管理、订单处理和用户分析三个页面&#xff0c;这时候标签页的流畅切换和状态保持就显得尤为重要。 我经历过一…...

突破信息获取壁垒:智能内容解锁工具使用指南

突破信息获取壁垒&#xff1a;智能内容解锁工具使用指南 【免费下载链接】bypass-paywalls-chrome-clean 项目地址: https://gitcode.com/GitHub_Trending/by/bypass-paywalls-chrome-clean 你是否曾遇到这样的情况&#xff1a;一篇专业的技术文章、一份重要的研究报告…...