SpringBoot 实现数据加密脱敏(注解 + 反射 + AOP)

SpringBoot 实现数据加密脱敏(注解 + 反射 + AOP)

场景:响应政府要求,商业软件应保证用户基本信息不被泄露,不能直接展示用户手机号,身份证,地址等敏感信息。

根据上面场景描述,我们可以分析出两个点。

  • 不被泄露说明用户信息应被加密储存;
  • 不能直接展示说明用户信息应脱敏展示;

解决方案

  • 傻瓜式编程:将项目中关于用户信息实体类的字段,比如姓名,手机号,身份证,地址等,在新增进数据库之前,对数据进行加密处理;在列表中展示用户信息时,对数据库中的数据进行解密脱敏,然后返回给前端;

  • 切入式编程:将项目中关于用户信息实体类的字段用注解给标记,新增用户信息实体类(这里我们用UserBO来表示,给UserBO里面的name,phone字段添加@EncryptField),返回用户信息实体类(这里我们用UserDO来表示,给UserDO里面的name,phone字段添加@DecryptField);然后利用@EncryptField,@DecryptField做为切入点,以切面的形式实现加密,解密脱敏;

傻瓜式编程不是说傻,而是相当于切入式编程,傻瓜式编程需要对用户信息相关的所有接口进行加密,解密脱敏的逻辑处理,这里改动的地方就比较多,风险高,重复操作相同的逻辑,工作量大,后期不好维护;切入式编程只需要对用户信息字段添加注解,对有注解的字段统一进行加密,解密脱敏逻辑处理,操作方便,高聚合,易维护;

方案实现

傻瓜式编程没什么难度,这里我给大家有切入式编程来实现;在实现之前,跟大家预热一下注解,反射,AOP的知识;

注解实战

创建注解

创建一个只能标记在方法上的注解:

package com.weige.javaskillpoint.annotation;import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;@Target(ElementType.METHOD)         //METHOD 说明该注解只能用在方法上
@Retention(RetentionPolicy.RUNTIME) //RUNTIME 说明该注解在运行时生效
public @interface Encryption {}

创建一个只能标记在字段上的注解:

package com.weige.javaskillpoint.annotation;import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;@Target(ElementType.FIELD)           //FIELD 说明该注解只能用在字段上
@Retention(RetentionPolicy.RUNTIME)  //RUNTIME 说明该注解在运行时生效
public @interface EncryptField {}

创建一个标记在字段上,且有值的注解:

package com.weige.javaskillpoint.annotation;import com.weige.javaskillpoint.enums.DesensitizationEnum;import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface DecryptField {// 注解是可以有值的,这里可以为数组,String,枚举等类型// DesensitizationEnum desensitizationEnum = field.getAnnotation(DecryptField.class).value(); 这里的field是指当前标记的字段DesensitizationEnum value(); 
}
注解使用

创建枚举

package com.weige.javaskillpoint.enums;public enum DesensitizationEnum {name,     // 用户信息姓名脱敏address,  // 用户信息地址脱敏phone;    // 用户信息手机号脱敏
}

创建UserDO类

package com.weige.javaskillpoint.entity;import com.weige.javaskillpoint.annotation.DecryptField;
import com.weige.javaskillpoint.enums.DesensitizationEnum;
import com.weige.javaskillpoint.utils.AesUtil;import java.lang.reflect.Field;// 用户信息返回实体类
public class UserDO {@DecryptField(DesensitizationEnum.name)private String name;@DecryptField(DesensitizationEnum.address)private String address;public String getName() {return name;}public void setName(String name) {this.name = name;}public String getAddress() {return address;}public void setAddress(String address) {this.address = address;}public UserDO(String name, String address) {this.name = name;this.address = address;}public static void main(String[] args) throws IllegalAccessException {// 生成并初始化对象UserDO userDO = new UserDO("梦想是什么","湖北省武汉市");// 反射获取当前对象的所有字段Field[] fields = userDO.getClass().getDeclaredFields();// 遍历字段for (Field field : fields) {// 判断字段上是否存在@DecryptField注解boolean hasSecureField = field.isAnnotationPresent(DecryptField.class);// 存在if (hasSecureField) {// 暴力破解 不然操作不了权限为private的字段field.setAccessible(true);// 如果当前字段在userDo中不为空 即name,address字段有值if (field.get(userDO) != null) {// 获取字段上注解的value值DesensitizationEnum desensitizationEnum = field.getAnnotation(DecryptField.class).value();// 控制台输出System.out.println(desensitizationEnum);// 根据不同的value值 我们可以对字段进行不同逻辑的脱敏 比如姓名脱敏-魏*,手机号脱敏-187****2275 }}}}
}

反射实战

创建UserBO类

package com.weige.javaskillpoint.entity;import com.weige.javaskillpoint.annotation.EncryptField;import java.lang.reflect.Field;// 用户信息新增实体类
public class UserBO {@EncryptFieldprivate String name;@EncryptFieldprivate String address;public String getName() {return name;}public void setName(String name) {this.name = name;}public String getAddress() {return address;}public void setAddress(String address) {this.address = address;}public UserBO(String name, String address) {this.name = name;this.address = address;}@Overridepublic String toString() {return "UserBO{" +"name='" + name + '\'' +", address='" + address + '\'' +'}';}public static void main(String[] args) throws IllegalAccessException {UserBO userBO = new UserBO("周传雄","湖北省武汉市");Field[] fields = userBO.getClass().getDeclaredFields();for (Field field : fields) {boolean annotationPresent = field.isAnnotationPresent(EncryptField.class);if(annotationPresent){// 当前字段内容不为空if(field.get(userBO) != null){// 这里对字段内容进行加密Object obj = encrypt(field.get(userBO));// 字段内容加密过后 通过反射重新赋给该字段field.set(userBO, obj);}}}System.out.println(userBO);}public static Object encrypt(Object obj){return "加密: " + obj;}
}

AOP实战

切入点:

package com.weige.javaskillpoint.controller;import com.weige.javaskillpoint.annotation.Encryption;
import com.weige.javaskillpoint.entity.UserBO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;@RestController
@RequestMapping("/encrypt")
@Slf4j
public class EncryptController {@PostMapping("/v1")@Encryption  // 切入点public UserBO insert(@RequestBody UserBO user) {log.info("加密后对象:{}", user);return user;}
}

切面:

package com.weige.javaskillpoint.aop;import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;@Slf4j
@Aspect
@Component
public class EncryptAspect {//拦截需加密注解 切入点@Pointcut("@annotation(com.weige.javaskillpoint.annotation.Encryption)")public void point() {}@Around("point()") //环绕通知public Object around(ProceedingJoinPoint joinPoint) throws Throwable {//加密逻辑处理encrypt(joinPoint);return joinPoint.proceed();}}

为什么这里要使用AOP:无论是注解,反射,都需要一个启动方法,我上面演示的是通过main函数来启动。使用AOP,项目启动后,只要调用切入点对应的方法,就会根据切入点来形成一个切面,进行统一的逻辑增强;如果大家熟悉SpringMVC,SpringMVC提供了 ResponseBodyAdvice 和 RequestBodyAdvice两个接口,这两个接口可以对请求和响应进行预处理,就可以不需要使用AOP;

加密解密脱敏实战

项目目录:

在这里插入图片描述

pom.xml文件:

<dependencies><!--Springboot项目自带 --><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><!--Springboot Web项目 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!--lombok --><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.22</version></dependency><!-- hutool  --><dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>5.7.20</version></dependency><!-- 切面 aop  --><dependency><groupId>org.aspectj</groupId><artifactId>aspectjweaver</artifactId><version>1.9.7</version></dependency></dependencies>
实体类

用户信息新增实体类 :UserBO

package com.weige.javaskillpoint.entity;import com.weige.javaskillpoint.annotation.EncryptField;// 实体类
public class UserBO {@EncryptFieldprivate String name;@EncryptFieldprivate String address;public String getName() {return name;}public void setName(String name) {this.name = name;}public String getAddress() {return address;}public void setAddress(String address) {this.address = address;}public UserBO(String name, String address) {this.name = name;this.address = address;}@Overridepublic String toString() {return "UserBO{" +"name='" + name + '\'' +", address='" + address + '\'' +'}';}
}

用户信息返回实体类 :UserDO

package com.weige.javaskillpoint.entity;import com.weige.javaskillpoint.annotation.DecryptField;
import com.weige.javaskillpoint.enums.DesensitizationEnum;// 实体类
public class UserDO {@DecryptField(DesensitizationEnum.name)private String name;@DecryptField(DesensitizationEnum.address)private String address;public String getName() {return name;}public void setName(String name) {this.name = name;}public String getAddress() {return address;}public void setAddress(String address) {this.address = address;}public UserDO(String name, String address) {this.name = name;this.address = address;}
}
脱敏枚举
package com.weige.javaskillpoint.enums;public enum DesensitizationEnum {name,address,phone;
}
注解

解密字段注解(字段):

package com.weige.javaskillpoint.annotation;import com.weige.javaskillpoint.enums.DesensitizationEnum;import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface DecryptField {DesensitizationEnum value();
}

解密方法注解(方法 作切入点):

package com.weige.javaskillpoint.annotation;import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Decryption {}

加密字段注解(字段):

package com.weige.javaskillpoint.annotation;import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface EncryptField {}

加密方法注解(方法 作切入点):

package com.weige.javaskillpoint.annotation;import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Encryption {}
控制层

解密 Controller:

package com.weige.javaskillpoint.controller;import com.weige.javaskillpoint.annotation.Decryption;
import com.weige.javaskillpoint.entity.UserDO;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;@RestController
@RequestMapping("/decrypt")
public class DecryptController {@GetMapping("/v1")@Decryptionpublic UserDO decrypt() {return new UserDO("7c29e296e92893476db5f9477480ba7f", "b5c7ff86ac36c01dda45d9ffb0bf73194b083937349c3901f571d42acdaa7bae");}}

加密 Controller:

package com.weige.javaskillpoint.controller;import com.weige.javaskillpoint.annotation.Encryption;
import com.weige.javaskillpoint.entity.UserBO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;@RestController
@RequestMapping("/encrypt")
@Slf4j
public class EncryptController {@PostMapping("/v1")@Encryptionpublic UserBO insert(@RequestBody UserBO user) {log.info("加密后对象:{}", user);return user;}
}
切面

解密脱敏切面:

package com.weige.javaskillpoint.aop;import com.weige.javaskillpoint.annotation.DecryptField;
import com.weige.javaskillpoint.enums.DesensitizationEnum;
import com.weige.javaskillpoint.utils.AesUtil;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Objects;@Slf4j
@Aspect
@Component
public class DecryptAspect {//拦截需解密注解@Pointcut("@annotation(com.weige.javaskillpoint.annotation.Decryption)")public void point() {}@Around("point()")public Object around(ProceedingJoinPoint joinPoint) throws Throwable {//解密return decrypt(joinPoint);}public Object decrypt(ProceedingJoinPoint joinPoint) {Object result = null;try {Object obj = joinPoint.proceed();if (obj != null) {//抛砖引玉 ,可自行扩展其他类型字段的判断if (obj instanceof String) {decryptValue();} else {result = decryptData(obj);}}} catch (Throwable e) {e.printStackTrace();}return result;}private Object decryptData(Object obj) throws IllegalAccessException {if (Objects.isNull(obj)) {return null;}if (obj instanceof ArrayList) {decryptList(obj);} else {decryptObj(obj);}return obj;}private void decryptObj(Object obj) throws IllegalAccessException {Field[] fields = obj.getClass().getDeclaredFields();for (Field field : fields) {boolean hasSecureField = field.isAnnotationPresent(DecryptField.class);if (hasSecureField) {field.setAccessible(true);if (field.get(obj) != null) {String realValue = (String) field.get(obj);DesensitizationEnum desensitizationEnum = field.getAnnotation(DecryptField.class).value();String value = (String) AesUtil.decrypt(realValue,desensitizationEnum);field.set(obj, value);}}}}private void decryptList(Object obj) throws IllegalAccessException {List<Object> result = new ArrayList<>();if (obj instanceof ArrayList) {result.addAll((Collection<?>) obj);}for (Object object : result) {decryptObj(object);}}private void decryptValue() {log.info("根据对象进行解密脱敏,单个字段不做处理!");}}

加密切面:

package com.weige.javaskillpoint.aop;import com.weige.javaskillpoint.annotation.EncryptField;
import com.weige.javaskillpoint.entity.UserBO;
import com.weige.javaskillpoint.utils.AesUtil;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;import java.lang.reflect.Field;@Slf4j
@Aspect
@Component
public class EncryptAspect {//拦截需加密注解@Pointcut("@annotation(com.weige.javaskillpoint.annotation.Encryption)")public void point() {}@Around("point()")public Object around(ProceedingJoinPoint joinPoint) throws Throwable {//加密encrypt(joinPoint);return joinPoint.proceed();}public void encrypt(ProceedingJoinPoint joinPoint) {Object[] objects;try {objects = joinPoint.getArgs();if (objects.length != 0) {for (Object object : objects) {if (object instanceof UserBO) {Field[] fields = object.getClass().getDeclaredFields();for (Field field : fields) {if (field.isAnnotationPresent(EncryptField.class)) {field.setAccessible(true);if (field.get(object) != null) {// 进行加密Object o = field.get(object);Object encrypt = AesUtil.encrypt(field.get(object));field.set(object, encrypt);}}}}}}} catch (Exception e) {log.error(e.getMessage());}}
}
工具类

加密工具类:AesUtil

package com.weige.javaskillpoint.utils;import cn.hutool.core.util.CharsetUtil;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.crypto.symmetric.AES;
import com.weige.javaskillpoint.enums.DesensitizationEnum;public class AesUtil {// 默认16位 或 128 256位public static String AES_KEY = "Wk#qerdfdshbd910";public static AES aes = SecureUtil.aes(AES_KEY.getBytes());public static Object encrypt(Object obj) {return aes.encryptHex((String) obj);}public static Object decrypt(Object obj, DesensitizationEnum desensitizationEnum) {// 解密Object decrypt = decrypt(obj);// 脱敏return DesensitizationUtil.desensitization(decrypt, desensitizationEnum);}public static Object decrypt(Object obj) {return aes.decryptStr((String) obj, CharsetUtil.CHARSET_UTF_8);}}

脱敏工具类:DesensitizationUtil

package com.weige.javaskillpoint.utils;import cn.hutool.core.util.StrUtil;
import com.weige.javaskillpoint.enums.DesensitizationEnum;public class DesensitizationUtil {public static Object desensitization(Object obj, DesensitizationEnum desensitizationEnum) {Object result;switch (desensitizationEnum) {case name:result = strUtilHide(obj, 1);break;case address:result = strUtilHide(obj, 3);break;default:result = "";}return result;}/*** start从0开始*/public static Object strUtilHide(String obj, int start, int end) {return StrUtil.hide(obj, start, end);}public static Object strUtilHide(Object obj, int start) {return strUtilHide(((String) obj), start, ((String) obj).length());}}

完结

以上代码不难,大伙复制到本地跑一遍,基本就能理解;愿每一位程序员少走弯路!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/73591.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

不同 vlan 之间互通

不同VLAN间的用户要实现互通 如果是不同网段用户&#xff0c;常用的技术为&#xff1a;vlanif 和 单臂路由都可以解决不同 vlan 之间三层包互通问题。 VLANIF VLANIF接口是一种三层的逻辑接口&#xff0c;能实现不同VLAN间&#xff0c;不同网段的用户进行三层互通。由于配置…

概率论与数理统计复习总结3

概率论与数理统计复习总结&#xff0c;仅供笔者复习使用&#xff0c;参考教材&#xff1a; 《概率论与数理统计》/ 荣腾中主编. — 第 2 版. 高等教育出版社《2024高途考研数学——概率基础精讲》王喆 概率论与数理统计实际上是两个互补的分支&#xff1a;概率论 在 已知随机…

【使用维纳滤波进行信号分离】基于维纳-霍普夫方程的信号分离或去噪维纳滤波器估计(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

error: #5: cannot open source input file “core_cmInstr.h“

GD32F103VET6和STM32F103VET6引脚兼容。 GD32F103VET6工程模板需要包含头文件&#xff1a;core_cmInstr.h和core_cmFunc.h&#xff0c;这个和STM32F103还是有区别的&#xff0c;否则会报错&#xff0c;如下&#xff1a; error: #5: cannot open source input file "core…

两个镜头、视野、分辨率不同的相机(rgb、红外)的视野校正

文章目录 背景实际效果查找资料资料1资料2 解决方案最终结果 背景 目前在做的项目用到两个摄像头&#xff0c;一个是热成像摄像头、另一个是普通的rgb摄像头。 一开始的目标是让他们像素级重合&#xff0c;使得点击rgb图像时&#xff0c;即可知道其像素对应的温度。但是在尝试…

js中的设计模式

设计模式 代码整体的结构会更加清楚&#xff0c;管理起来会更加方便&#xff0c;更好地维护 设计模式是一种思想 发布订阅 模块化开发 导入很多模块 容器即数组存储未来要执行的方法&#xff0c;同addEventListener 数组塌陷问题* 由于删除了元素&#xff0c;导致从删除元素的位…

ppt怎么压缩到10m以内?分享好用的压缩方法

PPT是一种常见的演示文稿格式&#xff0c;有时候文件过大&#xff0c;我们会遇到无法发送、上传的现象&#xff0c;这时候简单的解决方法就是压缩其大小&#xff0c;那怎么才能将PPT压缩到10M以内呢&#xff1f; PPT文件大小受到影响的主要因素就是以下几点&#xff1a; 1、图…

VR全景旅游,智慧文旅发展新趋势!

引言&#xff1a; VR全景旅游正在带领我们踏上一场全新的旅行体验。这种沉浸式的旅行方式&#xff0c;让我们可以足不出户&#xff0c;却又身临其境地感受世界各地的美景。 一&#xff0e;VR全景旅游是什么&#xff1f; VR全景旅游是一种借助虚拟现实技术&#xff0c;让用户…

AssetBundle学习

官方文档&#xff1a;AssetBundle 工作流程 - Unity 手册 (unity3d.com) 之前写的博客&#xff1a;AssetBundle学习_zaizai1007的博客-CSDN博客 使用流程图&#xff1a; 1&#xff0c;指定资源的AssetBundle属性 &#xff08;xxxa/xxx&#xff09;这里xxxa会生成目录&…

Arcgis 分区统计majority参数统计问题

利用Arcgis 进行分区统计时&#xff0c;需要统计不同矢量区域中栅格数据的众数&#xff08;majority&#xff09;&#xff0c;出现无法统计majority参数问题解决 解决&#xff1a;利用copy raster工具&#xff0c;将原始栅格数据 64bit转为16bit

iOS 应用上架流程详解

iOS 应用上架流程详解 欢迎来到我的博客&#xff0c;今天我将为大家分享 iOS 应用上架的详细流程。在这个数字化时代&#xff0c;移动应用已经成为了人们生活中不可或缺的一部分&#xff0c;而 iOS 平台的 App Store 则是开发者们发布应用的主要渠道之一。因此&#xff0c;了解…

Vision Transformer (ViT):图像分块、图像块嵌入、类别标记、QKV矩阵与自注意力机制的解析

作者&#xff1a;CSDN _养乐多_ 本文将介绍Vision Transformers &#xff08;ViT&#xff09;中的关键点。包括图像分块&#xff08;Image Patching&#xff09;、图像块嵌入&#xff08;Patch Embedding&#xff09;、类别标记、&#xff08;class_token&#xff09;、QKV矩…

微服务 云原生:搭建 K8S 集群

为节约时间和成本&#xff0c;仅供学习使用&#xff0c;直接在两台虚拟机上模拟 K8S 集群搭建 踩坑之旅 系统环境&#xff1a;CentOS-7-x86_64-Minimal-2009 镜像&#xff0c;为方便起见&#xff0c;直接在 root 账户下操作&#xff0c;现实情况最好不要这样做。 基础准备 关…

pycharm——涟漪散点图

from pyecharts import options as opts from pyecharts.charts import EffectScatterc (EffectScatter().add_xaxis( ["高等数学1&#xff0c;2","C语言程序设计","python程序设计","大数据导论","数据结构","大数据…

CentOS 8 上安装 Nginx

Nginx是一款高性能的开源Web服务器和反向代理服务器&#xff0c;以其轻量级和高效能而广受欢迎。在本教程中&#xff0c;我们将学习在 CentOS 8 操作系统上安装和配置 Nginx。 步骤 1&#xff1a;更新系统 在安装任何软件之前&#xff0c;让我们先更新系统的软件包列表和已安…

opencv 31-图像平滑处理-方框滤波cv2.boxFilter()

方框滤波&#xff08;Box Filtering&#xff09;是一种简单的图像平滑处理方法&#xff0c;它主要用于去除图像中的噪声和减少细节&#xff0c;同时保持图像的整体亮度分布。 方框滤波的原理很简单&#xff1a;对于图像中的每个像素&#xff0c;将其周围的一个固定大小的邻域内…

HTTP、HTTPS协议详解

文章目录 HTTP是什么报文结构请求头部响应头部 工作原理用户点击一个URL链接后&#xff0c;浏览器和web服务器会执行什么http的版本持久连接和非持久连接无状态与有状态Cookie和Sessionhttp方法&#xff1a;get和post的区别 状态码 HTTPS是什么ssl如何搞到证书nginx中的部署 加…

Sqli-labs1~65关 通关详解 解题思路+解题步骤+解析

Sqli-labs 01关 (web517) 输入?id1 正常 输入?id1 报错 .0 输入?id1-- 正常判断是字符型注入&#xff0c;闭合方式是这里插一句。limit 100,1是从第100条数据开始&#xff0c;读取1条数据。limit 6是读取前6条数据。 ?id1 order by 3-- 正常判断回显位有三个。?id…

ChatGPT在法律行业的市场潜力

​ChatGPT现在已经成为我们的文字生成辅助工具、搜索引擎助手&#xff0c;许多体验过它的朋友会发现对它越来越依赖&#xff0c;并将其逐渐融入到自己的日常工作、生活。但有一点值得注意&#xff1a;这种人工智能除了技术可行、经济价值可行还要与相关规范即人类普遍的价值观念…

轻松批量文件改名!一键翻译重命名文件夹/文件,省时高效!」

繁忙的数字时代&#xff0c;我们经常需要处理大量的文件和文件夹。而手动逐个更改文件名不仅费时费力&#xff0c;还容易出错。因此&#xff0c;我们为您带来了一款强大的工具——批量文件改名软件&#xff01;现在&#xff0c;您可以一键翻译重命名文件夹和文件&#xff0c;轻…