数据库脱敏(mybatis 拦截器实现)

article/2025/11/10 13:56:42

1.使用数据库本身自有的函数进行加密

UPDATE tuc_user SET mobileNo = HEX(AES_ENCRYPT(mobileNo, ‘xxxxxx’));

2.注解类

2.1 SensitiveData

package com.wisedu.campuses.sensitive;import java.lang.annotation.*;/*** @author MR.MEI*/
@Inherited
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface SensitiveData {}

2.2 EncryptField

package com.wisedu.campuses.sensitive;import java.lang.annotation.*;/*** @author MR.MEI*/
@Documented
@Inherited
@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
public @interface EncryptField {String value() default "";}

2.3 EncryptClass

package com.wisedu.campuses.sensitive;import java.lang.annotation.*;/*** @author MR.MEI*/
@Documented
@Inherited
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface EncryptClass {}

3.加密工具类

3.1 接口

package com.wisedu.campuses.sensitive;import java.lang.reflect.Field;
import java.util.List;/*** @author MR.MEI*/
public interface IEncryptUtil {/*** 加密** @param declaredFields 加密字段* @param paramsObject   对象* @param <T>            入参类型* @return 返回加密* @throws IllegalAccessException 不可访问*/<T> T encrypt(List<Field[]> declaredFields, T paramsObject) throws IllegalAccessException;
}

3.2 实现类

package com.wisedu.campuses.sensitive;import cn.hutool.core.lang.Validator;
import com.wisedu.campuses.utils.DBAESUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;
import javax.crypto.IllegalBlockSizeException;
import java.lang.reflect.Field;
import java.util.List;
import java.util.Objects;@Slf4j
@Component
public class EncryptUtilImpl implements IEncryptUtil {@Overridepublic <T> T encrypt(List<Field[]> declaredFields, T paramsObject) throws IllegalAccessException {//取出所有被EncryptTransaction注解的字段for (Field[] declaredField : declaredFields) {for (Field field : declaredField) {EncryptField encryptTransaction = field.getAnnotation(EncryptField.class);if (!Objects.isNull(encryptTransaction)) {field.setAccessible(true);Object object = field.get(paramsObject);//暂时只实现String类型的加密if (object instanceof String) {String value = (String) object;//加密try {if(StringUtils.isNotEmpty(value)){//防止重复加密if(Validator.isHex(value) && value.length()>=32){try {String decrypt = DBAESUtil.decrypt(value);if(StringUtils.isNotEmpty(decrypt)){value = decrypt;}}catch (IllegalBlockSizeException e){log.error(e.getMessage(), e);}}field.set(paramsObject, DBAESUtil.encrypt(value));}} catch (Exception e) {e.printStackTrace();}}}}}return paramsObject;}
}

4.解密工具类

4.1 接口

package com.wisedu.campuses.sensitive;/*** @author MR.MEI*/
public interface IDecryptUtil {/*** 解密** @param result resultType的实例* @return T* @throws IllegalAccessException 字段不可访问异常*/<T> T decrypt(T result) throws IllegalAccessException;
}

4.2 实现类

package com.wisedu.campuses.sensitive;import cn.hutool.core.lang.Validator;
import com.google.common.collect.Lists;
import com.wisedu.campuses.utils.DBAESUtil;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;
import java.lang.reflect.Field;
import java.util.List;
import java.util.Objects;@Component
public class DecryptImpl implements IDecryptUtil {/*** 解密** @param result resultType的实例*/@Overridepublic <T> T decrypt(T result) throws IllegalAccessException {List<Field[]> fieldsList = Lists.newArrayList();//取出resultType的类Class<?> resultClass = result.getClass();while (null != resultClass){fieldsList.add(resultClass.getDeclaredFields());resultClass = resultClass.getSuperclass();}for (Field[] fields : fieldsList) {for (Field field : fields) {EncryptClass encryptClass = field.getAnnotation(EncryptClass.class);if(!Objects.isNull(encryptClass)){if(field.getType() == List.class){Object o = field.get(result);field.setAccessible(true);List filedValue = (List) o;for (Object o1 : filedValue) {decrypt(o1);}}else {field.setAccessible(true);decrypt(field.get(result));}}//取出所有被DecryptTransaction注解的字段EncryptField encryptField = field.getAnnotation(EncryptField.class);if (!Objects.isNull(encryptField)) {field.setAccessible(true);Object object = field.get(result);//String的解密if (object instanceof String) {String value = (String) object;//对注解的字段进行逐一解密try {if(StringUtils.isNotEmpty(value)){if(Validator.isHex(value) && value.length()>=32)field.set(result, DBAESUtil.decrypt(value));}} catch (Exception e) {e.printStackTrace();}}}}}return result;}
}

5.加解密util

package com.wisedu.campuses.utils;import cn.hutool.crypto.SecureUtil;
import cn.hutool.crypto.symmetric.AES;
import java.nio.charset.StandardCharsets;/*** @author MR.MEI**/
public class DBAESUtil {private static final AES aes;static {aes = SecureUtil.aes("xxxxxx".getBytes(StandardCharsets.UTF_8));}/*** 加密*/public static String encrypt(String content) throws Exception {return aes.encryptHex(content);}/*** 解密*/public static String decrypt(String content) throws Exception {return aes.decryptStr(content);}}

6.参数拦截器

package com.wisedu.campuses.sensitive;import com.google.common.collect.Lists;
import com.wisedu.campuses.utils.DBAESUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.binding.MapperMethod;
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.apache.ibatis.scripting.defaults.DefaultParameterHandler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.sql.PreparedStatement;
import java.util.*;@Slf4j
@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 ,这里则能强转为ResultSetHandlerDefaultParameterHandler parameterHandler = (DefaultParameterHandler) 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) {if (parameterObject instanceof MapperMethod.ParamMap<?>) {MapperMethod.ParamMap<?> paramMap = (MapperMethod.ParamMap<?>) parameterObject;//支持批量处理if (paramMap.containsKey("list")) {Object objList = paramMap.get("list");if(objList instanceof List){List objs = (List)objList;for (Object obj : objs) {encryptObject(obj);}}}} else {encryptObject(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 encryptObject(Object parameterObject) throws IllegalAccessException {//对类字段进行加密//校验该实例的类是否被@SensitiveData所注解Class<?> parameterObjectClass = parameterObject.getClass();SensitiveData sensitiveData = AnnotationUtils.findAnnotation(parameterObjectClass, SensitiveData.class);if (Objects.nonNull(sensitiveData)) {List<Field[]> fieldsList = Lists.newArrayList();//取出当前当前类所有字段,传入加密方法while (null != parameterObjectClass){fieldsList.add(parameterObjectClass.getDeclaredFields());parameterObjectClass = parameterObjectClass.getSuperclass();}IEncryptUtil.encrypt(fieldsList, parameterObject);}}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<DefaultParameterHandler> handlerClass = DefaultParameterHandler.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.annotationType().equals(EncryptField.class)) {EncryptField encryptField = (EncryptField)annotation;if (paramNames == null) {paramNames = new ArrayList<>();}
//                        paramNames.add(parameters[i].getName());paramNames.add(encryptField.value());}}}}return paramNames;}@Overridepublic Object plugin(Object target) {return Plugin.wrap(target, this);}@Overridepublic void setProperties(Properties properties) {}
}

7.结果集拦截器

package com.wisedu.campuses.sensitive;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 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 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) {if(null == object){return false;}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) {}
}

8.配置

 <!-- mybaties --><bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"><!-- 加载mybatis的配置文件 --><property name="configLocation" value="classpath:SqlMapConfig.xml"/><!-- 数据源 --><property name="dataSource" ref="dataSource"/><!-- 自动扫描mapping.xml文件 --><property name="mapperLocations"><array><value>classpath*:mapper/**/*.xml</value></array></property><property name="plugins"><array><bean class="com.github.pagehelper.PageInterceptor"><property name="properties"><value>reasonable=true</value></property></bean><bean class="com.wisedu.campuses.sensitive.ParameterInterceptor"/><bean class="com.wisedu.campuses.sensitive.ResultSetInterceptor"/></array></property></bean>

9.具体的测试代码就不贴了,按照下面使用方式,对使用的类添加注解即可

脱敏注解

9.1 如果脱敏的字段在类中类,那么就需要使用@EncryptClass,如下图

脱敏注解

10.如遇到在业务方法中,对脱敏字段进行了更新后,使用同一对象类进行视图返回的,可以用以下方法作为补充处理,在controller层中增加(根据需要使用,非必要)@DesensitizeSupport注解即可

10.1 注解类

package com.wisedu.campuses.sensitive;import java.lang.annotation.*;@Documented
@Retention(value = RetentionPolicy.RUNTIME)
@Target(value={ElementType.METHOD, ElementType.TYPE})
@Inherited //说明子类可以继承父类中的该注解
public @interface DesensitizeSupport {}

10.2 视图拦截类

package com.wisedu.campuses.handle;import cn.hutool.core.lang.Validator;
import com.wisedu.campuses.sensitive.DesensitizeSupport;
import com.wisedu.campuses.sensitive.EncryptClass;
import com.wisedu.campuses.sensitive.EncryptField;
import com.wisedu.campuses.utils.DBAESUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;/*** 统一处理 返回值/响应体* 脱敏返回试图揭秘加强(可根据实际情况,按需在controller层上加注解)*/
@Slf4j
@ControllerAdvice
public class DesensitizeResponseBodyAdvice implements ResponseBodyAdvice<Object> {@Overridepublic boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {AnnotatedElement annotatedElement = returnType.getAnnotatedElement();//1.首先判断该方法是否存在@DesensitizeSupport注解//2.判断类上是否存在Method method = returnType.getMethod();DesensitizeSupport annotation = method.getAnnotation(DesensitizeSupport.class);DesensitizeSupport clazzSup = method.getDeclaringClass().getAnnotation(DesensitizeSupport.class);return annotation != null || clazzSup != null;}/**** @param body* @param returnType* @param selectedContentType* @param selectedConverterType* @param request* @param response* @return*/@Overridepublic Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType,Class<? extends HttpMessageConverter<?>> selectedConverterType,ServerHttpRequest request, ServerHttpResponse response) {log.debug("Test ResponseBodyAdvice ==> beforeBodyWrite:" + body.toString() + ";" + returnType);Class<?> childClazz = body.getClass();Field childField = null;
//        List filedValue = null;try {//获取数据childField = childClazz.getDeclaredField("data");//设置可访问childField.setAccessible(true);Object objs = childField.get(body);handlerObj(objs);} catch (NoSuchFieldException e) {log.error("未找到数据; message:" + e.getMessage());e.printStackTrace();} catch (IllegalAccessException e) {log.error("处理异常; message:" + e.getMessage());e.printStackTrace();} catch (Exception e){e.printStackTrace();}return body;}private void handlerObj(Object objs) throws Exception {List filedValue = null;if (!(objs instanceof List)) {dealValue(objs);}else {filedValue = (List) objs;//对值进行脱敏for (Object obj : filedValue) {dealValue(obj);}}}public void dealValue(Object obj) throws Exception {Class<?> clazz = obj.getClass();//获取奔雷和父类的属性List<Field> fieldList = getAllFields(clazz);for (Field field : fieldList) {//获取属性上的注解EncryptField encryptFieldAnnotation = field.getAnnotation(EncryptField.class);EncryptClass encryptClassAnnotation = field.getAnnotation(EncryptClass.class);if (encryptFieldAnnotation == null && encryptClassAnnotation == null) {continue;}Class<?> type = field.getType();if(type == List.class){field.setAccessible(true);Object o = field.get(obj);List filedValue = (List) o;for (Object o1 : filedValue) {dealValue(o1);}}//判断属性的类型if (String.class != type) {//非字符串的类型 直接返回continue;}//获取脱敏类型  判断是否脱敏field.setAccessible(true);String oldValue = (String) field.get(obj);if(StringUtils.isNotEmpty(oldValue) && Validator.isHex(oldValue) && oldValue.length()>=32){field.set(obj, DBAESUtil.decrypt(oldValue));}}}/*** 获取所有的字段(包括父类的)* @param clazz* @return*/public List<Field> getAllFields(Class<?> clazz) {List<Field> fieldList = new ArrayList<>();while (clazz != null) {Field[] declaredFields = clazz.getDeclaredFields();fieldList.addAll(Arrays.asList(declaredFields));//获取父类,然后获取父类的属性clazz = clazz.getSuperclass();}return fieldList;}
}

10.3 使用方式

	@DesensitizeSupport@RequestMapping(value = "/invoice/detail", method = RequestMethod.POST)@ResponseBodypublic Result detail(@Valid @RequestBody OpenInvoiceQueryDTO openInvoiceQueryDTO, BindingResult bindingResult) {......}

http://chatgpt.dhexx.cn/article/RKJQBoeF.shtml

相关文章

数据库安全关键技术之数据库脱敏技术详解

数据库脱敏是一种采用专门的脱敏算法对敏感数据进行变形、屏蔽、替换、随机化、加密&#xff0c;并将敏感数据转化为虚构数据的技术。按照作用位置、实现原理不同&#xff0c;数据脱敏可以划分为静态数据脱敏&#xff08;Static Data Masking, SDM )和动态数据脱敏&#xff08;…

网页数据库设计

这次是找一个网页,写er图,然后根据er图设计模型,然后生成数据库表 我找的是起点中文网,根据这个网站做了分析,画了e-r图. 模型

Java网页应用之实现对数据库的增、删、改、查。

模拟信息管理系统 通过eclipse实现网页、数据库的连接&#xff0c;实现对数据库的增、删、改、查。 登陆页面&#xff1a; 登陆成功界面&#xff1a; 添加信息界面&#xff1a; 修改信息界面&#xff1a; 删除信息界面&#xff1a; package com.lq.pro_user.dao;import …

(网页设计+数据库增删查改)——课设展示

注&#xff1a;关于网页所有的代码&#xff0c;我已经打包好了&#xff0c;获取方式如下 关注微信公众号大数据智库&#xff08;直接扫二维码&#xff0c;在我的主页的左下角&#xff09;&#xff0c;回复网页代码即可获取 课设展示 一、网页部分成果展示1、动态显示日期2、动态…

网页实现数据库的增删改查

最近在做项目web后台数据增删查改的时候&#xff0c;看到一篇较为详细的经典文章&#xff0c; 所以转载了下来&#xff0c; 文章出处在&#xff1a;https://blog.csdn.net/qq_32539825/article/details/70657340 如果作者认为侵权的告诉我&#xff0c;我立马删。 首先jsp 和…

通过JSP网页连接Mysql数据库

本文介绍通过JSP网页连接到MySQL,从MySQL数据库中读出一张表&#xff0c;并显示在JSP网页中。 1. 在MySQL数据库中建立数据表 用图形化管理工具Navicat Premium 连接MySQL数据库&#xff0c;在数据库“ming”下建立一张名为“teacher”的数据表。 2. 设置Tomcat 1)将JDBC…

网页连接mysql教程_网页怎么连接到数据库?

网页连接到数据库的方法&#xff1a;首先创建一个网页连接数据库的PHP代码文件&#xff1b;然后解决浏览器编码问题并指定数据库字符集&#xff1b;最后通过“mysql_select_db”函数选择并连接数据库即可。 HTML是无法读取数据库的&#xff0c;HTML是页面前端脚本语言&#xff…

网页JS自动化脚本(八)使用网页专属数据库indexedDB进行数据收集

我们在网页上进行的活动,往往都需要进行收集一些简单的数据,但是因为浏览器的安全原因,浏览器基本上是无法与本地的操作系统直接产生数据交互的,这本来就是一个由于安全问题生产的无解问题,在浏览器里面是内置了几种数据库的,其中一种就是indexedDB,可以用来储存一些非常小的数…

网页连接数据库,一个简单的登入界面以及实现登入功能

基于V#的ASP.NET.MVC 4 web 网站程序开发 接着上篇继续&#xff0c;上篇地址为https://blog.csdn.net/weixin_42534390/article/details/86576537 我们首先要有一个认知&#xff0c;就是ASP.NET.MVC 4 web 网站程序开发的三层架构概念&#xff0c;分别为BLL,DAL,MOD 这三层的概…

JavaWeb 网页链接数据库增删改查

今天是web的第五节课&#xff0c;上一节课只涉及到了查询和增加&#xff0c;这一节课我们学习网页链接数据库进行增删改查&#xff0c;今天的内容更多的也是代码&#xff0c;理论内容较少&#xff0c;现在小编带大家进入新的一课。 目录 一.查 二.增 三.删 四.修 五.数据库…

jsp登录注册代码(增删改查+网页+数据库)

目录 一登录注册代码以及效果 doregister.jsp:注册信息弹框 login.jsp:登录 dologin.jsp&#xff1a;与数据库相连、存放登陆的用户 index.jsp:主界面 update.jsp:修改 doup.jsp:修改页面&#xff08;帮助&#xff09; info.jsp&#xff1a;详情 dodel.jsp:删除界面 …

采集网页数据保存到MYSQL数据库

一、直接采集一个本地网页文件 1.创建一个网页文件date413.html(注&#xff1a;文件的路径后面还会用到) 2.网页文件date413.html中的内容为&#xff1a; <html> <head><title>搜索指数</title> </head> <body><table><tr>&…

用简单,易懂的方法将数据库和网页连接起来(例:登陆注册页面)

PHP技术&#xff0c;远比用java写代码连接数据库简单得多&#xff0c;下面看我的详细介绍&#xff1a; 1.首先要下载&#xff1a;phpstudy_pro&#xff08;关注我可以免费下载哦&#xff01;&#xff09; 2.然后打开phpstudy_pro&#xff0c;启动Apache服务器&#xff08;每次…

网页展示数据库内容

目的&#xff1a;写一个JSP访问Access数据库的user表&#xff0c;将所有的记录显示出来&#xff1b;ODBC数据源名为test&#xff0c;驱动类名为&#xff1a;“driverClassNamecom.mysql.jdbc.Driverr”&#xff0c;连接数据库的url为&#xff1a;”urljdbc:mysql://localhost:3…

[VS]网页连接数据库

工具&#xff1a; 1、Visual Studio(我使用的是vs2019) 2、SQL server(我使用的是sql2008) 3、网页运行框架&#xff1a;.Net2.0 目录 一、网站以windows身份验证连接数据库 二、网站以sql server身份验证连接数据库 三、使用工具Visual Studio中自带的连接数据库功能 一…

网页开发(十四)—数据库管理

书接上回&#xff1a; 7、MySQL指令 MySQL普通认知数据库文件夹数据表文件&#xff08;Excel文件&#xff09; 7.1 数据库管理&#xff08;文件夹&#xff09; &#xff08;1&#xff09;查看已有的数据库&#xff08;文件夹&#xff09;&#xff1a;show databases; &…

每周一品 · 音圈电机(VCM)中的磁性材料

音圈电机 (Voice coil motor, VCM) 是一种特殊形式的直驱电机&#xff0c;能将电能直接转化为直线运动机械能&#xff0c;其原理是在均匀气隙磁场中放入一个圆筒绕组&#xff0c;绕组通电产生磁场&#xff0c;带动负载设备做直线往复运动&#xff0c;改变电流的强弱和流向&…

VCM音圈马达的一些特点

VCM(Voice Coil Motor)&#xff1a;中文名称为音圈马达&#xff0c;亦称音圈电机&#xff0c;原理是在一个固定的磁场内&#xff0c;通过改变VCM线圈电流的大小&#xff0c;来控制VCM马达移动的位置&#xff0c;从而改变镜片之间的距离来达到对焦的功能。基本上在所有手机上均有…

vcm驱动芯片原理_每周一品 · 音圈电机(VCM)中的磁性材料

音圈电机 (Voice coil motor, VCM) 是一种特殊形式的直驱电机&#xff0c;能将电能直接转化为直线运动机械能&#xff0c;其原理是在均匀气隙磁场中放入一个圆筒绕组&#xff0c;绕组通电产生磁场&#xff0c;带动负载设备做直线往复运动&#xff0c;改变电流的强弱和流向&…

Camera基础知识

物理结构 一、手机Camerade的物理结构 FPC&#xff08;flexible printed circuit&#xff09;可挠性印刷电路板Sensor图像传感器IR红外i滤波片holder基座lens镜头 通常&#xff0c;一个摄像头硬件上包括5个部分&#xff1a;外壳&#xff08;马达&#xff09;、镜头&#xff…