SpringBoot请求参数加密、响应参数解密
SpringBoot请求参数加密、响应参数解密
1.说明
在项目开发工程中,有的项目可能对参数安全要求比较高,在整个http数据传输的过程中都需要对请求参数、响应参数进行加密,也就是说整个请求响应的过程都是加密处理的,不在浏览器上暴露请求参数、响应参数的真实数据。
补充:
也可以用于单点登录,在请求参数中添加时间戳,后台解析请求参数对时间戳进行校验,比如当前时间和请求参数中的时间戳相差多少秒、分钟才能进行放行,返回token。这样做的好处在于请求端每次加密之后的密文都是变化的,也能够避免携带相同的报文可以重复的登录。
2.准备工作
1.引入依赖, 创建SpringBoot工程
<dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></dependency><dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId></dependency><dependency><groupId>commons-io</groupId><artifactId>commons-io</artifactId><version>2.11.0</version></dependency><dependency><groupId>junit</groupId><artifactId>junit</artifactId></dependency><dependency><groupId>commons-codec</groupId><artifactId>commons-codec</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId></dependency><!--swagger--><dependency><groupId>io.springfox</groupId><artifactId>springfox-swagger2</artifactId><version>3.0.0</version></dependency><dependency><groupId>io.springfox</groupId><artifactId>springfox-swagger-ui</artifactId><version>3.0.0</version></dependency><dependency><groupId>io.swagger</groupId><artifactId>swagger-annotations</artifactId><version>1.5.22</version></dependency><dependency><groupId>com.github.xiaoymin</groupId><artifactId>swagger-bootstrap-ui</artifactId><version>1.8.7</version></dependency><dependency><groupId>org.apache.commons</groupId><artifactId>commons-lang3</artifactId></dependency></dependencies>
3.代码实现
1.定义两个注解
/*** @description: : 请求参数解密*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DecryptionAnnotation {
}/*** @description: 响应参数加密*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface EncryptionAnnotation {
}
2.加密解密实现核心代码
DecryptRequestBodyAdvice:请求参数解密,针对post请求
package com.llp.crypto.advice;import cn.hutool.json.JSONUtil;
import com.llp.crypto.annotation.DecryptionAnnotation;
import com.llp.crypto.utils.AESUtil;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.springframework.core.MethodParameter;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpInputMessage;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.servlet.mvc.method.annotation.RequestBodyAdvice;import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Type;/*** @description: 请求参数解密,针对post请求*/@Slf4j
@ControllerAdvice
public class DecryptRequestBodyAdvice implements RequestBodyAdvice {/*** 方法上有DecryptionAnnotation注解的,进入此拦截器** @param methodParameter 方法参数对象* @param targetType 参数的类型* @param converterType 消息转换器* @return true,进入,false,跳过*/@Overridepublic boolean supports(MethodParameter methodParameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {return methodParameter.hasMethodAnnotation(DecryptionAnnotation.class);}@Overridepublic HttpInputMessage beforeBodyRead(HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) throws IOException {try {return new MyHttpInputMessage(inputMessage, parameter);} catch (Exception e) {throw new RuntimeException(e);}}/*** 转换之后,执行此方法,解密,赋值** @param body spring解析完的参数* @param inputMessage 输入参数* @param parameter 参数对象* @param targetType 参数类型* @param converterType 消息转换类型* @return 真实的参数*/@SneakyThrows@Overridepublic Object afterBodyRead(Object body, HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {log.info("解密后的请求报文:{}", body);return body;}/*** 如果body为空,转为空对象** @param body spring解析完的参数* @param inputMessage 输入参数* @param parameter 参数对象* @param targetType 参数类型* @param converterType 消息转换类型* @return 真实的参数*/@Overridepublic Object handleEmptyBody(Object body, HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {return body;}class MyHttpInputMessage implements HttpInputMessage {private HttpHeaders headers;private InputStream body;private MethodParameter parameter;public MyHttpInputMessage(HttpInputMessage inputMessage, MethodParameter parameter) throws Exception {this.headers = inputMessage.getHeaders();//只对post请求进行加密if (parameter.hasMethodAnnotation(PostMapping.class)) {/**请求报文示例:* {* "requestData":"JF7kvl9Wd/vgdmAS8JijsQ=="* }*/String decrypt = AESUtil.decrypt(easpData(IOUtils.toString(inputMessage.getBody(), "UTF-8")));log.info("解密后的请求参数:{}", decrypt);this.body = IOUtils.toInputStream(decrypt, "UTF-8");} else {this.body = inputMessage.getBody();}}@Overridepublic InputStream getBody() throws IOException {return body;}@Overridepublic HttpHeaders getHeaders() {return headers;}}public String easpData(String requestData) {if (requestData != null && !requestData.equals("")) {String start = "requestData";if (requestData.contains(start)) {return JSONUtil.parseObj(requestData).getStr(start);} else {throw new RuntimeException("参数【requestData】缺失异常!");}}return "";}
}
GetDeleteDecryptAspect:针对get、delete请求参数进行解密
@Aspect
//值越小优先级越高
@Order(-1)
@Component
@Slf4j
public class GetDeleteDecryptAspect {/*** 对get、delete方法进行解密* @param point* @return* @throws Throwable*/@Around("@annotation(com.llp.crypto.annotation.DecryptionAnnotation) && " + "(@annotation(org.springframework.web.bind.annotation.GetMapping) || @annotation(org.springframework.web.bind.annotation.DeleteMapping))")public Object aroundMethod(ProceedingJoinPoint point) throws Throwable {MethodSignature signature = (MethodSignature) point.getSignature();Method method = signature.getMethod();// 获取到请求的参数列表Object[] args = point.getArgs();// 判断方法请求参数是否需要解密if (method.isAnnotationPresent(DecryptionAnnotation.class)) {try {this.decrypt(args, point);log.info("返回解密结果=" + args);} catch (Exception e) {e.printStackTrace();log.error("对方法method :【" + method.getName() + "】入参数据进行解密出现异常:" + e.getMessage());}}// 执行将解密的结果交给控制器进行处理,并返回处理结果return point.proceed(args);}/*** 前端对请求参数进行加密,最终将这个加密的字符串已 localhost:48080?data=xxx这样的方式进行传递* 后端后去到 data的数据进行解密最终得到解密后的数据* @param args* @param point* @throws Exception*/// 解密方法@SuppressWarnings("unchecked")public void decrypt(Object[] args, ProceedingJoinPoint point) throws Exception {ServletRequestAttributes sc = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();HttpServletRequest request = sc.getRequest();String data = request.getParameter("data");log.info("data: " + data);// 将密文解密为JSON字符串Class<?> aClass = args[0].getClass();log.info("数据类型:{}",aClass.getClass());if (StringUtils.isNotEmpty(data)) {// 将JSON字符串转换为Map集合,并替换原本的参数args[0] = JSONUtil.toBean(AESUtil.decrypt(data), args[0].getClass());}}
}
EncryptResponseBodyAdvice:响应参数解密,针对统一返回结果类的装配
/*** @description: 响应加密*/@Slf4j
@ControllerAdvice
public class EncryptResponseBodyAdvice implements ResponseBodyAdvice {@Overridepublic boolean supports(MethodParameter methodParameter, Class aClass) {return methodParameter.hasMethodAnnotation(EncryptionAnnotation.class);}@Overridepublic Object beforeBodyWrite(Object body, MethodParameter methodParameter, MediaType mediaType, Class aClass, ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) {log.info("对方法method :【" + methodParameter.getMethod().getName() + "】返回数据进行加密");// 只针对回参类型为CommonResult的对象,进行加密if (body instanceof CommonResult) {CommonResult commonResult = (CommonResult) body;Object data = commonResult.getData();if (Objects.nonNull(data)) {// 将响应结果转换为json格式String result = JSONUtil.toJsonStr(data);log.info("返回结果:{}", result);try {String encrypt = AESUtil.encrypt(result);commonResult.setData(encrypt);log.info("返回结果加密=" + commonResult);} catch (Exception e) {log.error("对方法method :【" + methodParameter.getMethod().getName() + "】返回数据进行解密出现异常:" + e.getMessage());}return commonResult;}}return body;}}
3.统一返回结果
@Data
public class CommonResult<T> {private String code;private String msg;private T data;public CommonResult() {}public CommonResult(T data) {this.data = data;}/*** 表示成功的Result,不携带返回数据** @return*/public static CommonResult success() {CommonResult result = new CommonResult();result.setCode("200");result.setMsg("success");return result;}/*** 便是成功的Result,携带返回数据* 如果需要在static方法使用泛型,需要在static后指定泛型表示 static<T>** @param data* @return*/public static <T> CommonResult<T> success(T data) {CommonResult<T> result = new CommonResult<>(data);result.setCode("200");result.setMsg("success");return result;}/*** 失败不携带数据* 将错误的code、msg作为形参,灵活传入** @param code* @param msg* @return*/public static CommonResult error(String code, String msg) {CommonResult result = new CommonResult();result.setCode(code);result.setMsg(msg);return result;}/*** 失败携带数据* 将错误的code、msg、data作为形参,灵活传入* @param code* @param msg* @param data* @param <T>* @return*/public static <T> CommonResult<T> error(String code, String msg, T data) {CommonResult<T> result = new CommonResult<>(data);result.setCode(code);result.setMsg(msg);return result;}}
4.加密工具类
public class AESUtil {// 加解密方式private static final String AES_ALGORITHM = "AES/ECB/PKCS5Padding";// 与前端统一好KEYprivate static final String KEY = "abcdsxyzhkj12345";// 获取 cipherprivate static Cipher getCipher(byte[] key, int model) throws Exception {SecretKeySpec secretKeySpec = new SecretKeySpec(KEY.getBytes(), "AES");Cipher cipher = Cipher.getInstance(AES_ALGORITHM);cipher.init(model, secretKeySpec);return cipher;}// AES加密public static String encrypt(String data) throws Exception {Cipher cipher = getCipher(KEY.getBytes(), Cipher.ENCRYPT_MODE);return Base64.getEncoder().encodeToString(cipher.doFinal(data.getBytes("UTF-8")));}// AES解密public static String decrypt(String data) throws Exception {Cipher cipher = getCipher(KEY.getBytes(), Cipher.DECRYPT_MODE);return new String(cipher.doFinal(Base64.getDecoder().decode(data.getBytes("UTF-8"))),"UTF-8");}public static byte[] decryptUrl(String url) throws Exception {Cipher cipher = getCipher(KEY.getBytes(), Cipher.DECRYPT_MODE);return cipher.doFinal(Base64.getDecoder().decode(url.replaceAll(" +", "+")));}// AES解密MySQL AES_ENCRYPT函数加密密文public static String aesDecryptMySQL(String key, String content){try {SecretKey secretKey = generateMySQLAESKey(key,"ASCII");Cipher cipher = Cipher.getInstance("AES");cipher.init(Cipher.DECRYPT_MODE, secretKey);byte[] cleartext = Hex.decodeHex(content.toCharArray());byte[] ciphertextBytes = cipher.doFinal(cleartext);return new String(ciphertextBytes, StandardCharsets.UTF_8);} catch (Exception e) {e.printStackTrace();}return null;}//加密public static String aesEncryptMySQL(String key2, String content) {try {SecretKey key = generateMySQLAESKey(key2,"ASCII");Cipher cipher = Cipher.getInstance("AES");cipher.init(Cipher.ENCRYPT_MODE, key);byte[] cleartext = content.getBytes("UTF-8");byte[] ciphertextBytes = cipher.doFinal(cleartext);return new String(Hex.encodeHex(ciphertextBytes));} catch (Exception e) {e.printStackTrace();}return null;}public static SecretKeySpec generateMySQLAESKey(final String key, final String encoding) {try {final byte[] finalKey = new byte[16];int i = 0;for(byte b : key.getBytes(encoding)) {finalKey[i++%16] ^= b;}return new SecretKeySpec(finalKey, "AES");} catch(UnsupportedEncodingException e) {throw new RuntimeException(e);}}@Testpublic void decodeTest() {try {String a = "{\"username\":\"admin\",\"deptId\":\"1250500000\",\"userId\":\"1\",\"phone\":\"15195928695\"}";String encrypt = AESUtil.encrypt(a);System.out.println("加密后的字符串: "+encrypt);System.out.println("解密后的字符串:" +AESUtil.decrypt(encrypt));String str = "5tAayXF5ZcPC9yoNvBIT0fw2Li2uoxUhGyMq4JKUvCttOFnU7iKovyB9pm/ZV+2qU8h2htdk5s6ht9kCpTGG9WZAGTdMUgIJkD/Tf6IQ3gw=";String decrypt = AESUtil.decrypt(IOUtils.toString(str.getBytes(), "UTF-8"));System.out.println(decrypt);} catch (Exception e) {e.printStackTrace();}}
}
5.请求流支持多次获取
/*** 请求流支持多次获取*/
public class InputStreamHttpServletRequestWrapper extends HttpServletRequestWrapper {/*** 用于缓存输入流*/private ByteArrayOutputStream cachedBytes;public InputStreamHttpServletRequestWrapper(HttpServletRequest request) {super(request);}@Overridepublic ServletInputStream getInputStream() throws IOException {if (cachedBytes == null) {// 首次获取流时,将流放入 缓存输入流 中cacheInputStream();}// 从 缓存输入流 中获取流并返回return new CachedServletInputStream(cachedBytes.toByteArray());}@Overridepublic BufferedReader getReader() throws IOException {return new BufferedReader(new InputStreamReader(getInputStream()));}/*** 首次获取流时,将流放入 缓存输入流 中*/private void cacheInputStream() throws IOException {// 缓存输入流以便多次读取。为了方便, 我使用 org.apache.commons IOUtilscachedBytes = new ByteArrayOutputStream();IOUtils.copy(super.getInputStream(), cachedBytes);}/*** 读取缓存的请求正文的输入流* <p>* 用于根据 缓存输入流 创建一个可返回的*/public static class CachedServletInputStream extends ServletInputStream {private final ByteArrayInputStream input;public CachedServletInputStream(byte[] buf) {// 从缓存的请求正文创建一个新的输入流input = new ByteArrayInputStream(buf);}@Overridepublic boolean isFinished() {return false;}@Overridepublic boolean isReady() {return false;}@Overridepublic void setReadListener(ReadListener listener) {}@Overridepublic int read() throws IOException {return input.read();}}}
4.测试
1.测试类
@Slf4j
@RestController
@Api(tags = "测试加密解密")
public class TestController {/*** 请求示例:* {* "requestData":"5tAayXF5ZcPC9yoNvBIT0fw2Li2uoxUhGyMq4JKUvCttOFnU7iKovyB9pm/ZV+2qU8h2htdk5s6ht9kCpTGG9WZAGTdMUgIJkD/Tf6IQ3gw="* }** @return*/@PostMapping(value = "/postEncrypt")@ApiOperation("测试post加密")@EncryptionAnnotation@DecryptionAnnotationpublic CommonResult<String> postEncrypt(@RequestBody UserReqVO userReqVO) {System.out.println("userReqVO: ============>" + userReqVO);return CommonResult.success("成功");}@GetMapping(value = "/getEncrypt")@ApiOperation("测试get加密")@DecryptionAnnotation // requestBody 自动解密public CommonResult<UserReqVO> getEncrypt(String data) {log.info("解密后的数据:{}",data);UserReqVO userReqVO = JSONUtil.toBean(data, UserReqVO.class);//UserReqVO(username=admin, deptId=1250500000, userId=1, phone=15195928695)log.info("用户信息:{}",userReqVO);return CommonResult.success(userReqVO);}
}
@ApiModel(description = "用户请求vo")
@Data
public class UserReqVO {@ApiModelProperty(value = "用户名", required = true)private String username;@ApiModelProperty(value = "部门id",required = true)private Long deptId;@ApiModelProperty(value = "用户id",required = true)private Long userId;@ApiModelProperty(value = "电话号码",required = true)private String phone;}
测试结果
相关文章:

SpringBoot请求参数加密、响应参数解密
SpringBoot请求参数加密、响应参数解密 1.说明 在项目开发工程中,有的项目可能对参数安全要求比较高,在整个http数据传输的过程中都需要对请求参数、响应参数进行加密,也就是说整个请求响应的过程都是加密处理的,不在浏览器上暴…...
Mysql适配国产化数据库人大金仓冲突记录
1、mysql中查询中如果使用双引号,在人大金仓数据库中不支持,需改为单引号 例如: select 字段A,字段B,字段C from tableA where 字段A "1" 改为: select 字段A,字段B,字段…...

在微服务架构中认证和授权的那些事儿
在微服务架构中认证和授权是最基础的服务能力,其中这一块行业类的标准就是OAuth2 和 SSO ,而OAuth2 和 SSO 可以归类为“用户管理和身份验证”工具,OpenID Connect 1.0是 OAuth 2.0 协议之上的一个简单身份层。 Part.1 认识OAuth 2.0 OAuth…...
Git使用统一规范
为什么要统一git使用的风格? 统一的风格使我们在工作的时候无需考虑工作流程上该如何去做的问题,按照一个风格去做就好了每个人风格不同,格式凌乱,查看很不方便commit没有准确的message,后续难以追踪问题 git messag…...
如何在前端优化中处理大量的图像资源?
在前端优化中,处理大量的图像资源是一项重要的任务。由于图像占据了网站带宽的大部分,因此优化图像可以显著提高网站的性能和用户体验。下面将介绍一些在前端优化中处理大量图像资源的常见方法。 一、压缩图像 压缩图像是减少图像文件大小和优化图像的…...
【MYSQL】性能相关
SQL 语句的性能分析是一个非常重要的任务,尤其是在处理大数据时。下面是一些常用的 SQL 性能分析方法: 执行计划: 使用 EXPLAIN 命令来查看 SQL 语句的执行计划。这可以帮助你了解查询是如何被数据库执行的,从而发现可能的性能瓶颈。 注意&…...

【Jmeter之get请求传递的值为JSON体实践】
Jmeter之get请求传递的值为JSON体实践 get请求的常见传参方式 1、在URL地址后面拼接,有多个key和value时,用&链接 2、在Parameters里面加上key和value 第一次遇到value的值不是字符串也不是整型,我尝试把json放到value里面࿰…...

(1)(1.13) SiK无线电高级配置(六)
文章目录 前言 15 使用FTDI转USB调试线配置SiK无线电设备 16 强制启动加载程序模式 17 名词解释 前言 本文提供 SiK 遥测无线电(SiK Telemetry Radio)的高级配置信息。它面向"高级用户"和希望更好地了解无线电如何运行的用户。 15 使用FTDI转USB调试线配置SiK无线…...
用JAVA实现樱花飘落
用java实现一个樱花飘落的方法 package Text2;import javax.swing.*; import java.awt.*; import java.util.ArrayList; import java.util.List;public class Sakura extends JFrame {private List<Point> sakuraList; // 樱花的位置列表public Sakura() {sakuraList n…...

Web开发:SQLsugar的安装和使用
一、安装 第一步,在你的项目中找到解决方案,右键-管理解决方案的Nuget 第二步,下载对应的包,注意你的框架是哪个就下载哪个的包,一个项目安装一次包即可 点击应用和确定 安装好后会显示sqlsugar的包 二、使用…...
Redis面试题10
Redis 支持哪些数据结构? Redis 支持以下几种常用的数据结构: 字符串(String):用于存储字符串值,可以是文本或二进制数据。 列表(List):用于存储一个有序的字符串列表&am…...
arm64架构编译electron长征路
文章目录 1. gn工具生成1.1 问题,找不到last_commit_position.h文件问题描述如下:解决方法1.2 ninja文件不是对应架构问题问题描述:解决方法1.3 问题3:clang++找不到问题描述解决方法2. electron 编译参数生成2.1 下载对应版本debian_bullseye_arm64-sysroot错误描述...

建模软件Rhinoceros mac介绍说明
Rhinoceros mac是一款3D设计软件“犀牛”,在当今众多三维建模软件中,Rhinoceros 版因为其体积小、功能强大、对硬件要求低而广受欢迎,对于专业的3D设计人员来说它是一款不错的3D建模软件,Rhinoceros Mac中文版能轻易整合3DS MAX与…...

视频号下载小助手:教你微信视频号怎么提取视频出来
作为一名剪辑师或自由职业者,我们作为短视频创作者有时候需要下载多个视频用于制作多个解说系列的视频或者连续剧。然而,下载这些视频通常需要花费大量时间和精力,尤其是在没有合适的工具的情况下,让我们制作视频也确实困难,那么我们该如何解决呢&#x…...
C#-委托
委托类型 (delegate type) 表示对具有特定参数列表和返回类型的方法的引用。通过委托,我们能够将方法作为实体赋值给变量和作为参数传递。委托类似于在其他某些语言中的函数指针的概念,但是与函数指针不同,委托是面向对象的,并且是…...

Mr_HJ / form-generator项目文档学习与记录(续2)
更多ruoyi-nbcio功能请看演示系统 gitee源代码地址 前后端代码: https://gitee.com/nbacheng/ruoyi-nbcio 演示地址:RuoYi-Nbcio后台管理系统 更多nbcio-boot功能请看演示系统 gitee源代码地址 后端代码: https://gitee.com/nbacheng/n…...
React16源码: React中FiberRoot的源码实现
关于 FiberRoot 1 )概述 在 ReactDOM.render 过程当中,创建了一个 ReactRoot的对象这个 ReactRoot 对象最主要承担了创建 FiberRoot 对象这个对象它非常重要,在后期整个应用调度过程当中都会跟它有关关于 FiberRoot 对象 A. 它是整个应用的起…...

Linux第24步_安装windows下的VisualStudioCode软件
Visual Stuio Code是一个编辑器,简称 为 VSCode,它是微软出的一款免费编辑器。 VSCode有 Windows、 Linux和 macOS三个版本的,是一个跨平台的编辑器。VSCodeUserSetup-x64-1.50.1是Windows系统中的VSCode软件,而“code_1.50.1-160…...
Spring 注解 和SpringMVC注解
Spring和Spring MVC是两个紧密相关但又不同的框架,它们都使用一系列注解来简化开发。以下是Spring和Spring MVC中一些常用的注解: ### Spring 注解: 1. **Component:** - 用于将类标记为Spring容器中的组件,由Spr…...

iOS rootless无根越狱解决方案
据游戏工委数据统计,2023年国内游戏市场实际销售收入与用户规模双双创下新高,游戏普遍采用多端并发方式,成为收入增长的主因之一。 中国市场实际销售收入及增长率丨数据来源:游戏工委 多端互通既是机遇,也是挑战。从游…...

label-studio的使用教程(导入本地路径)
文章目录 1. 准备环境2. 脚本启动2.1 Windows2.2 Linux 3. 安装label-studio机器学习后端3.1 pip安装(推荐)3.2 GitHub仓库安装 4. 后端配置4.1 yolo环境4.2 引入后端模型4.3 修改脚本4.4 启动后端 5. 标注工程5.1 创建工程5.2 配置图片路径5.3 配置工程类型标签5.4 配置模型5.…...

Unity3D中Gfx.WaitForPresent优化方案
前言 在Unity中,Gfx.WaitForPresent占用CPU过高通常表示主线程在等待GPU完成渲染(即CPU被阻塞),这表明存在GPU瓶颈或垂直同步/帧率设置问题。以下是系统的优化方案: 对惹,这里有一个游戏开发交流小组&…...
PHP和Node.js哪个更爽?
先说结论,rust完胜。 php:laravel,swoole,webman,最开始在苏宁的时候写了几年php,当时觉得php真的是世界上最好的语言,因为当初活在舒适圈里,不愿意跳出来,就好比当初活在…...

iPhone密码忘记了办?iPhoneUnlocker,iPhone解锁工具Aiseesoft iPhone Unlocker 高级注册版分享
平时用 iPhone 的时候,难免会碰到解锁的麻烦事。比如密码忘了、人脸识别 / 指纹识别突然不灵,或者买了二手 iPhone 却被原来的 iCloud 账号锁住,这时候就需要靠谱的解锁工具来帮忙了。Aiseesoft iPhone Unlocker 就是专门解决这些问题的软件&…...
条件运算符
C中的三目运算符(也称条件运算符,英文:ternary operator)是一种简洁的条件选择语句,语法如下: 条件表达式 ? 表达式1 : 表达式2• 如果“条件表达式”为true,则整个表达式的结果为“表达式1”…...
多模态商品数据接口:融合图像、语音与文字的下一代商品详情体验
一、多模态商品数据接口的技术架构 (一)多模态数据融合引擎 跨模态语义对齐 通过Transformer架构实现图像、语音、文字的语义关联。例如,当用户上传一张“蓝色连衣裙”的图片时,接口可自动提取图像中的颜色(RGB值&…...

涂鸦T5AI手搓语音、emoji、otto机器人从入门到实战
“🤖手搓TuyaAI语音指令 😍秒变表情包大师,让萌系Otto机器人🔥玩出智能新花样!开整!” 🤖 Otto机器人 → 直接点明主体 手搓TuyaAI语音 → 强调 自主编程/自定义 语音控制(TuyaAI…...

论文笔记——相干体技术在裂缝预测中的应用研究
目录 相关地震知识补充地震数据的认识地震几何属性 相干体算法定义基本原理第一代相干体技术:基于互相关的相干体技术(Correlation)第二代相干体技术:基于相似的相干体技术(Semblance)基于多道相似的相干体…...
Redis:现代应用开发的高效内存数据存储利器
一、Redis的起源与发展 Redis最初由意大利程序员Salvatore Sanfilippo在2009年开发,其初衷是为了满足他自己的一个项目需求,即需要一个高性能的键值存储系统来解决传统数据库在高并发场景下的性能瓶颈。随着项目的开源,Redis凭借其简单易用、…...

【 java 虚拟机知识 第一篇 】
目录 1.内存模型 1.1.JVM内存模型的介绍 1.2.堆和栈的区别 1.3.栈的存储细节 1.4.堆的部分 1.5.程序计数器的作用 1.6.方法区的内容 1.7.字符串池 1.8.引用类型 1.9.内存泄漏与内存溢出 1.10.会出现内存溢出的结构 1.内存模型 1.1.JVM内存模型的介绍 内存模型主要分…...