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

接口对外安全交互新姿势

文章目录

  • 1.前言
  • 2.姿势
    • 2.1 AES
    • 2.2 body参数签名及验签
    • 2.3使用sm2 加ip白名单
  • 3.总结

1.前言

    由于这久做了一个乐企数电开票的项目,已经上线了,真的是一言难尽,再回首已经是轻舟已过万重山,接口通过外网暴露给业务方使用,由于业务方的服务是在阿里云上,我的这个服务是在华为云上,所以k8s上的服务只能通过service对外暴露出去给阿里云上的业务侧使用,所以我就有了下面的这个思路,使用ip白名单和sm2对请求body的做解密,响应做加密,只需要把秘钥和加密工具类对给业务侧就可以安全的调用接口,其实在华为云上可以在负载均衡器上配置白名单,只允许阿里云上那几台服务器的ip访问,所以在项目中就不用加这个ip白名单,那如果在没有云的情况下,这个方案也是可以的,安全性更高且可以灵活配置,本文介绍三种方式,至于其它的方式还有很多,毕竟密码学是一门高深的学科,常用的加密算法有MD5,AES(加密在前端使用会被找到秘钥),RSA(文本太长就会有点慢的,这个我之前也试过的,网上也有好多的工具类,去搜几个来调试一下就可以用了,之前搞的那个懒得去找了,后面有时间找一下看看分享一波),SM国密系列的,可以说非常的多,但是常用的就这几种。

2.姿势

2.1 AES

https://www.jianshu.com/p/f9284c3f732c

    之前我分享那个Hutool工具包中搞了一个工具类,对接前端的CryptoJS实现是无法使用的,我那个工具类之适用于后端client和server之间的的加密解密交互,vue的CryptoJS实现后端加密前端解密需要参看上面那篇博客,在项目中亲测是有效的,这种方式的弊端就是AES的秘钥在前端的源码中会被轻易的找到,导致key泄露,从而可以轻松抓取到接口的参数并使用网页上的key解密看到解密之后的明文数据,这种方式也不推荐使用,安全性太差。

2.2 body参数签名及验签

    这种方式只适用于接口数据不重要的情况下使用一个sign的参数,将body中的其它map参数进行排序之后签名,被调用端收掉body中的sign之后,对body中的参数进行验签,这种方式可能判断参数是否被篡改,数据明文传输就类似于裸奔,所以这种方式不推荐使用,安全性太差。

SignUtils工具类代码如下:

package xxx.xxx.xxx.util;import org.apache.commons.lang3.StringUtils;import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import java.util.Map;/*** Description:签名工具类*/
public class SignUtils {public static String getSign(Map<String, Object> requestMap, String appKey) {return hmacSHA256Encrypt(requestMap2Str(requestMap), appKey);}private static String hmacSHA256Encrypt(String encryptText, String encryptKey) {byte[] result = null;try {//根据给定的字节数组构造一个密钥,第二参数指定一个密钥算法的名称SecretKeySpec signinKey = new SecretKeySpec(encryptKey.getBytes(StandardCharsets.UTF_8), "HmacSHA256");//生成一个指定 Mac 算法 的 Mac 对象Mac mac = Mac.getInstance("HmacSHA256");//用给定密钥初始化 Mac 对象mac.init(signinKey);//完成 Mac 操作byte[] rawHmac = mac.doFinal(encryptText.getBytes(StandardCharsets.UTF_8));return bytesToHexString(rawHmac);} catch (Exception e) {e.printStackTrace();}return null;}private static String requestMap2Str(Map<String, Object> requestMap) {String[] keys = requestMap.keySet().toArray(new String[0]);Arrays.sort(keys);StringBuilder stringBuilder = new StringBuilder();for (String str : keys) {if (!str.equals("sign")) {stringBuilder.append(str).append(requestMap.get(str).toString());}}return stringBuilder.toString();}public static String bytesToHexString(byte[] bArray) {StringBuffer sb = new StringBuffer(bArray.length);for (byte aBArray : bArray) {String sTemp = Integer.toHexString(255 & aBArray);if (sTemp.length() < 2) {sb.append(0);}sb.append(sTemp.toUpperCase());}return sb.toString();}/*** MD5生成签名字符串** @param str 需签名参数* @return*/public static String md5(String str) {if (StringUtils.isEmpty(str)) {return "";}MessageDigest md5 = null;try {md5 = MessageDigest.getInstance("MD5");byte[] bytes = md5.digest(str.getBytes());String result = "";for (byte b : bytes) {String temp = Integer.toHexString(b & 0xff);if (temp.length() == 1) {temp = "0" + temp;}result += temp;}return result;} catch (NoSuchAlgorithmException e) {e.printStackTrace();}return "";}
}

2.3使用sm2 加ip白名单

    这个思路是来源于hutool官网:

https://doc.hutool.cn/pages/SmUtil/#%E5%AF%B9%E7%A7%B0%E5%8A%A0%E5%AF%86sm4

    需要引入Bouncy Castle依赖

<dependency><groupId>org.bouncycastle</groupId><artifactId>bcpkix-jdk18on</artifactId><version>1.78.1</version>
</dependency>

说明 bcprov-jdk18on的版本请前往Maven中央库搜索,查找对应JDK的最新版本。

    body中栗子如下:

{"appId":"xxxx",//哪个业务的标识"method":"xxxx",//调用那个方法,被调用端可以将其它接口全部聚合到一个接口上对外暴露出去"encryptStr":"04A51E4C0CA2766B9F382C7D308AB7E8C3DD1E104CAF83D95C72C46154F6D1ACBCD199A4BAC57C9FF9EF9864728BFE20F8568ADDD1C1BD51ECFF24D6F37095091F55AE3A3C1518B308092E8338D4A8BDD87DEC5C68DB6373A0D50709AD82B0FA484CEE169FD7DA7156133E396FAE77E315DE49D7F3F99BE4701B1686763F026412DEA8334123D8A3E186B16B4E806D4E866229029973F34D9A298F715304AEDEE069A6E1CBBF86D3757DC4A02F66EBE7652E64465081313C6242F67687F48D67A29C61F2ED6C115F627DD032331A7F04D1475B3D8A86D640BA8C64E310B4689EFA835943F7C3057B057E5B888A0E87D167EAE2F4028564406C2A08ACF3516453B099A89CE66AF4634079F79E710182E967530ACF1E4D39A349F3083CDD5E05ACB5A866BE49164BD3C26A163ADD8D2BA7ADF499EEAF220B9545B67CE654D80C84E51E02D3975633812B7FFF97E65179FBB13B74D337615E8D04C9C1A534F554DA1F9D626BDC4EE985D9326986A07618EDA24623AE254EA5FC3A32E79443E8ED5F15C8D586D91752AC488CD0600F6509F422A11B7766CD1FA2332BC28B3E676A50985292E6CE6FB7891B358AACAB94C974EA34437DD8154F4CB79B35BA0AE5588562E5F93255FA5BF60742E82EB530E379B7117B12EBFD42B6D2643884FD360350B4AB92D5C11F04D326C1EB8C0674A972755C25B06B73209653A922C2BF0F64EB1750453E4308D6E791BAC903786ECABF493E0B19118AA7A76D8422C1FBDD1191B401FB113551460AE301383A10C23D18994E00FFBB99059B05D31F86F3DDED25C5B45CAB08EDA46FBA357DF5CEBEE9846D81D6A06B308466BE44EB0DD2800A457C8AF698CCBE3D2C0B0D013F830E6F5153BA95EFC10C98597909D9BDE39B14C30B6AEEE274D80A576326D04774C43B84778B2724E2344F54E793487023823FA0F5F6506CBC9E49DD25852D7DD2918AB440EE27975456DF80F65CECC51167929E0B96A4F98D770959166B348F0F202173A5EE7B3D8CED79E8BB4757A877F97D725BED91E07634B45B0E890D860277C15D9ADFA87C2EEDD359D47048BEA1AEF7A568E5F6EFFE9C530C5B67A7C94D67BC2FE3D065C50C2384C786DF637A39E827D2BCE31BD493D8874498C7F75C3F75DA5E8ED300DB9842059D923E9DD136500863121324A909BA3A8"
}

    被调用端服务只需要配置一个对应业务的秘钥对且是否启用该秘钥对,如果秘钥对泄露,重新在数据库里面加一对新的秘钥对且启用密钥对,将之前的秘钥对停用了,这种就可以保证接口的安全性。

IpUtil工具类:

package xxxxx.xxxx.util;import lombok.extern.slf4j.Slf4j;import javax.servlet.http.HttpServletRequest;/*** 常用获取客户端信息的工具*/
@Slf4j
public final class IpUtil {/*** 获取请求主机IP地址,如果通过代理进来,则透过防火墙获取真实IP地址;** @param request* @return*/public static String getIpAddress(HttpServletRequest request) {// 获取请求主机IP地址,如果通过代理进来,则透过防火墙获取真实IP地址String ip = request.getHeader("X-Forwarded-For");log.info("getIpAddress(HttpServletRequest) - X-Forwarded-For - String ip=" + ip);if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {ip = request.getHeader("Proxy-Client-IP");log.info("getIpAddress(HttpServletRequest) - Proxy-Client-IP - String ip=" + ip);}if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {ip = request.getHeader("WL-Proxy-Client-IP");log.info("getIpAddress(HttpServletRequest) - WL-Proxy-Client-IP - String ip=" + ip);}if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {ip = request.getHeader("HTTP_CLIENT_IP");log.info("getIpAddress(HttpServletRequest) - HTTP_CLIENT_IP - String ip=" + ip);}if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {ip = request.getHeader("HTTP_X_FORWARDED_FOR");log.info("getIpAddress(HttpServletRequest) - HTTP_X_FORWARDED_FOR - String ip=" + ip);}if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {ip = request.getRemoteAddr();log.info("getIpAddress(HttpServletRequest) - getRemoteAddr - String ip=" + ip);}} else if (ip.length() > 15) {String[] ips = ip.split(",");for (int index = 0; index < ips.length; index++) {String strIp = (String) ips[index];if (!("unknown".equalsIgnoreCase(strIp))) {ip = strIp;break;}}}return ip;}}

    WhiteIpConfig白名单配置类:

package xxx.xxxx.config;import xxxx.xxxx.IpUtil;
import lombok.Data;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;import javax.servlet.http.HttpServletRequest;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;@Data
@Configuration
@RefreshScope
public class WhiteIpConfig {@Value("${whiteIp:192.168.40.60}")private String whiteIp;public List<String> getWhiteIps() {List<String> appIds = new ArrayList<>();if (StringUtils.isNotEmpty(whiteIp)) {String[] split = whiteIp.split(",");appIds = Arrays.asList(split);}return appIds;}public void checkWhiteIp() {HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();List<String> whiteIps = this.getWhiteIps();String ipAddress = IpUtil.getIpAddress(request);if (StringUtils.isEmpty(ipAddress)) {throw new RuntimeException("未获取到请求的ip地址!");}if (!whiteIps.contains(ipAddress)) {throw new RuntimeException("请求ip地址不在ip白名单内,不允许访问!");}}}

    还需要对响应的数据进行sm2加密,接收响应端使用密钥对进行解密就可以拿到响应的明文数据了,这种接口就非常的安全。

    这里在分享一个采集服务器的ip地址和mac地址的工具类,亲测好用:

    NetUtil工具类:

package xxxx.xxxx.util;import lombok.extern.slf4j.Slf4j;import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;@Slf4j
public class NetUtil {/*** 此方法描述的是:获得服务器的IP地址** @return*/public static String getLocalIP() {String sIP = "";InetAddress ip = null;try {boolean bFindIP = false;Enumeration<NetworkInterface> netInterfaces = (Enumeration<NetworkInterface>) NetworkInterface.getNetworkInterfaces();while (netInterfaces.hasMoreElements()) {if (bFindIP) {break;}NetworkInterface ni = (NetworkInterface) netInterfaces.nextElement();Enumeration<InetAddress> ips = ni.getInetAddresses();while (ips.hasMoreElements()) {ip = (InetAddress) ips.nextElement();if (!ip.isLoopbackAddress()&& ip.getHostAddress().matches("(\\d{1,3}\\.){3}\\d{1,3}")) {bFindIP = true;break;}}}} catch (Exception e) {log.error("getLocalIP.ex:{}", e.getMessage());}if (null != ip) {sIP = ip.getHostAddress();}return sIP;}/*** 此方法描述的是:获得服务器的IP地址(多网卡)** @return*/public static List<String> getLocalIPS() {InetAddress ip = null;List<String> ipList = new ArrayList<String>();try {Enumeration<NetworkInterface> netInterfaces = (Enumeration<NetworkInterface>) NetworkInterface.getNetworkInterfaces();while (netInterfaces.hasMoreElements()) {NetworkInterface ni = (NetworkInterface) netInterfaces.nextElement();Enumeration<InetAddress> ips = ni.getInetAddresses();while (ips.hasMoreElements()) {ip = (InetAddress) ips.nextElement();if (!ip.isLoopbackAddress()&& ip.getHostAddress().matches("(\\d{1,3}\\.){3}\\d{1,3}")) {ipList.add(ip.getHostAddress());}}}} catch (Exception e) {log.error("getLocalIPS.ex:{}", e.getMessage());}return ipList;}/*** 此方法描述的是:获得服务器的MAC地址** @return*/public static String getMacId() {String macId = "";InetAddress ip = null;NetworkInterface ni = null;try {boolean bFindIP = false;Enumeration<NetworkInterface> netInterfaces = (Enumeration<NetworkInterface>) NetworkInterface.getNetworkInterfaces();while (netInterfaces.hasMoreElements()) {if (bFindIP) {break;}ni = (NetworkInterface) netInterfaces.nextElement();// ----------特定情况,可以考虑用ni.getName判断// 遍历所有ipEnumeration<InetAddress> ips = ni.getInetAddresses();while (ips.hasMoreElements()) {ip = (InetAddress) ips.nextElement();if (!ip.isLoopbackAddress() // 非127.0.0.1&& ip.getHostAddress().matches("(\\d{1,3}\\.){3}\\d{1,3}")) {bFindIP = true;break;}}}} catch (Exception e) {log.error("getMacId.ex1:{}", e.getMessage());}if (null != ip) {try {macId = getMacFromBytes(ni.getHardwareAddress());} catch (SocketException e) {log.error("getMacId.ex2:{}", e.getMessage());}}return macId;}/*** 此方法描述的是:获得服务器的MAC地址(多网卡)** @return*/public static List<String> getMacIds() {InetAddress ip = null;NetworkInterface ni = null;List<String> macList = new ArrayList<String>();try {Enumeration<NetworkInterface> netInterfaces = (Enumeration<NetworkInterface>) NetworkInterface.getNetworkInterfaces();while (netInterfaces.hasMoreElements()) {ni = (NetworkInterface) netInterfaces.nextElement();// ----------特定情况,可以考虑用ni.getName判断// 遍历所有ipEnumeration<InetAddress> ips = ni.getInetAddresses();while (ips.hasMoreElements()) {ip = (InetAddress) ips.nextElement();if (!ip.isLoopbackAddress() // 非127.0.0.1&& ip.getHostAddress().matches("(\\d{1,3}\\.){3}\\d{1,3}")) {macList.add(getMacFromBytes(ni.getHardwareAddress()));}}}} catch (Exception e) {log.error("getMacIds.ex2:{}", e.getMessage());}return macList;}private static String getMacFromBytes(byte[] bytes) {StringBuffer mac = new StringBuffer();byte currentByte;boolean first = false;for (byte b : bytes) {if (first) {mac.append("-");}currentByte = (byte) ((b & 240) >> 4);mac.append(Integer.toHexString(currentByte));currentByte = (byte) (b & 15);mac.append(Integer.toHexString(currentByte));first = true;}return mac.toString().toUpperCase();}public static void main(String[] args) {String localIP = NetUtil.getLocalIP();String macId = NetUtil.getMacId();log.info("localIP:{},macId:{}", localIP, macId);}}

3.总结

    本次分享到此结束,希望我的分享对你有所启发和帮助,思路基本上都是大同小异,套路基本上是一个套路,主要是的是姿势,姿势真的很重要,姿势不对努力白费,好久没有写文章了,请尊重作者原创,转载请注明出处,否则发现抄袭直接举报,创作不易,请一键三连,么么么哒!

相关文章:

接口对外安全交互新姿势

文章目录 1.前言2.姿势2.1 AES2.2 body参数签名及验签2.3使用sm2 加ip白名单 3.总结 1.前言 由于这久做了一个乐企数电开票的项目&#xff0c;已经上线了&#xff0c;真的是一言难尽&#xff0c;再回首已经是轻舟已过万重山&#xff0c;接口通过外网暴露给业务方使用&#xff0…...

Docker基础篇——Ubuntu下Docker安装

大家好我是木木&#xff0c;在当今快速发展的云计算与云原生时代&#xff0c;容器化技术蓬勃兴起&#xff0c;Docker 作为实现容器化的主流工具之一&#xff0c;为开发者和运维人员带来了极大的便捷 。下面我们一起进行Docker安装。 Docker的官方Ubuntu安装文档&#xff0c;如…...

《深度解析DeepSeek-M8:量子经典融合,重塑计算能效格局》

在科技飞速发展的今天&#xff0c;量子计算与经典算法的融合成为了前沿领域的焦点。DeepSeek-M8的“量子神经网络混合架构”&#xff0c;宛如一把钥匙&#xff0c;开启了经典算法与量子计算协同推理的全新大门&#xff0c;为诸多复杂问题的解决提供了前所未有的思路。 量子计算…...

关于C/C++语言的初学者在哪刷题,怎么刷题

引言&#xff1a; 这篇博客主要是针对初学者关于怎么在网上刷题&#xff0c;以及在哪里刷题。 1.介绍平台&#xff08;在哪刷题&#xff09;&#xff1a; 1.牛客牛客网https://www.nowcoder.com/ &#xff1a;有许多面试题&#xff0c;也有许多供学习者练习的题 2.洛谷洛谷 …...

【redis】string类型相关操作:SET、GET、MSET、MGET、SETNX、SETEX、PSETEX

文章目录 二进制存储编码转换SET 和 GETSETGET MSET 和 MGETSETNX、SETEX 和 PSETEX Redis 所有的 key 都是字符串&#xff0c;value 的类型是存在差异的 二进制存储 Redis 中的字符串&#xff0c;直接就是按照二进制数据的方式存储的 不仅仅可以存储文本数据&#xff0c;还可…...

el-table中嵌套了el-form-item 导致的内容不垂直居中展示的问题

el-table中嵌套了el-form-item 导致的内容不垂直居中展示的问题 这个问题原先我一直没有找到问题的关键点&#xff0c;后来看了一篇文章得知由于el-form-item的margin导致的 下面的css类告诉我们。正常的表单校验margin就是20px&#xff0c;在el-table中的只有是校验失败的才会…...

LVCMOS(Low Voltage Complementary Metal-Oxide-Semiconductor)电平详解

一、LVCMOS电平的定义与核心特性 LVCMOS&#xff08;低压互补金属氧化物半导体&#xff09;是 CMOS技术的低电压版本&#xff0c;专为现代低功耗、高集成度芯片设计&#xff0c;支持 1.2V、1.8V、2.5V、3.3V 等多种电压等级。其通过优化晶体管结构和供电电压&#xff0c;显著降…...

计算机操作系统(一) 什么是操作系统

计算机操作系统&#xff08;一&#xff09; 什么是操作系统 前言一、什么是操作系统二、操作系统的作用三、推动操作系统发展的主要动力总结&#xff08;核心概念速记&#xff09;&#xff1a; 前言 当你打开电脑、点击应用、播放音乐时&#xff0c;是谁在背后默默协调这一切&…...

《用 python、MySQL 和 Chart.js 打造炫酷数据看板》实战案例笔记

今天&#xff0c;我们要构建一个数据看板系统。在这个过程中&#xff0c;我们会利用 MySQL 来存储数据&#xff0c;使用 Python 搭建后端 API&#xff0c;还会借助 Chart.js 在前端呈现各式各样的图表。 整个流程涵盖多个环节&#xff0c;首先要进行数据库表的设计&#xff0c…...

Android ANR 监控方法与事件分发耗时优化实战

一、ANR 监控方法 &#xff08;一&#xff09;系统日志分析 系统日志始终是查找 ANR 根源的重要依据。利用日志分析&#xff0c;不仅可以锁定 ANR 发生的精确时刻&#xff0c;还能追踪到主线程、关键函数调用的阻塞细节。 日志关键词检索&#xff1a;利用 ADB 命令&#xff…...

【蓝桥杯单片机】第十一届省赛

一、真题 二、创建工程 1.在C盘以外的盘新建文件夹&#xff0c;并在文件夹里面创建两个文件夹Driver 和Project 2.打开keil软件&#xff0c;在新建工程并选择刚刚建好的project文件夹&#xff0c;以准考证号命名 3.选择对应的芯片型号 4.选择否&#xff0c;即不创建启动文件 …...

【ES6】模块化

概述 模块功能主要有两个命令&#xff0c;export和import。 一个js文件就是一个模块。 参考视频 【一小时速通JavaScript模块化&#xff0c;涵盖CommonJS与ES6模块化-哔哩哔哩】 https://b23.tv/gZ1uK7V 导出成员 在正常变量、函数前加export关键字。 导入模块 在另一个…...

C++学习——顺序表(六)

文章目录 前言一、找到数组的中间位置二、有序数组中的单一元素三、杨辉三角&#xff08;Ⅱ&#xff09;四、超过阈值的最小操作数Ⅰ五、找出峰值六、统计已测试设备七、统计和小于目标的下标对数目1.单向遍历法2.双指针法&#xff08;时间复杂度小&#xff09; 八、计算K置位下…...

python迭代器生成器

迭代器生成器区别 通俗版概念 ​迭代器&#xff08;Iterator&#xff09;​ ​像“快递员送快递”​&#xff1a; 你有一个包裹清单&#xff08;比如Excel里的测试用例&#xff09;&#xff0c;快递员&#xff08;迭代器&#xff09;会按顺序一个一个送&#xff08;遍历&#x…...

Hive SQL 精进系列:字符串拼接的三种常用方式

Hive字符串拼接&#xff1a;三种常用方式深度剖析 目录 Hive字符串拼接&#xff1a;三种常用方式深度剖析引言一、简洁直观的||操作符1. 基础语法规则2. 丰富多样的示例展示3. 优势与局限分析 二、规范通用的CONCAT函数1. 全面的语法解析2. 生动的示例说明3. 优势与局限剖析 三…...

MATLAB—从入门到精通的第二天

在第一天的学习中&#xff0c;我们掌握了 MATLAB 的安装配置、基础语法、变量管理和运算符的使用。本文将深入讲解 控制结构&#xff08;嵌套 if、switch&#xff09;、循环类型 和 向量操作&#xff0c;帮助读者进一步掌握 MATLAB 的核心编程技能。 1. 条件语句进阶 1.1 嵌套…...

韦伯望远镜的拉格朗日点计算推导过程,包含MATLAB和python运动轨迹仿真代码

研究过程 起源与提出&#xff1a;1687 年牛顿提出 “三体问题”&#xff0c;旨在研究三个可视为质点的天体在相互之间万有引力作用下的运动规律&#xff0c;但因运动方程过于复杂&#xff0c;难以得到完全解。欧拉的贡献1&#xff1a;1767 年&#xff0c;瑞士数学家莱昂哈德・…...

【 现代后端架构演进:微服务设计与云原生】

现代后端架构演进&#xff1a;微服务设计与云原生 一、架构演进历程 1. 单体架构到分布式系统 单体架构瓶颈 典型问题&#xff1a;代码耦合&#xff08;代码行超百万级&#xff09;、扩展困难&#xff08;垂直扩容成本 > 1 0 5 >10^5 >105美元/节点&#xff09;、技术…...

[JAVASE] 注解

一. 注解是什么? 注解是一种为程序元素提供元数据的方法.注解就是为程序做特殊标记的. 二. java内置的注解 分别是: 作用在代码的注解是: Override - 检查该方法是否是重写方法。如果发现其父类&#xff0c;或者是引用的接口中并没有该方法时&#xff0c;会报编译错误。 De…...

热成像仪真不错

我挂在外面的网路设备箱 室内的机柜 室外的猫 所用型号为优利德UTi160S&#xff0c;显示模式为&#xff08;可见光与热成像&#xff09;融合模式。...

Vue-Virtual-Scroller虚拟滚动

前端优化不可不避的一谈之虚拟滚动&#xff1a;众所周知&#xff0c;滚动是直挺挺的往dom树加东西&#xff0c;如果滚太多滚到万级&#xff0c;渲染过多就会卡顿&#xff0c;而vue-virtual-scroll的灵活懒渲染就能解决这个问题 1&#xff0c;下载与配置 npm install --save v…...

Matlab:矩阵运算篇——矩阵

目录 1.定义 实例——创建矩阵 实例——创建复数矩阵 2.矩阵的生成 实例——M文件矩阵 2.利用文本创建 实例——创建生活用品矩阵 3.创建特殊矩阵 实例——生成特殊矩阵 4.矩阵元素的运算 1.矩阵元素的修改 实例——新矩阵的生成 2.矩阵的变维 实例——矩阵维度修…...

[Java]使用java进行JDBC编程

首先要从中央仓库下载api(类似驱动程序)&#xff0c;为了链接java和mysql 下载jar包&#xff0c;需要注意的是jar包的版本要和mysql保持一致 下面是新建文件夹lib&#xff0c;把jar包放进去&#xff0c;并添加为库 sql固定的情况下运行 import com.mysql.cj.jdbc.MysqlDataSo…...

HippoRAG 2 原理精读

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 整体流程离线索引阶段在线检索和问答阶段 总结 整体流程 从上图可以看出&#xff0c;整个流程分为两个阶段 1、离线索引阶段 2、在线检索和问答阶段 离线索引阶段…...

HTTPS协议原理:在Linux世界里的加密冒险

大家好&#xff0c;欢迎来到这次奇妙的HTTPS协议探险之旅&#xff01;今天&#xff0c;我们将一起潜入Linux的深处&#xff0c;揭开HTTPS协议那神秘而迷人的面纱。别担心&#xff0c;即使你是技术小白&#xff0c;也能在这场冒险中找到乐趣和收获。想象一下&#xff0c;你是一位…...

Spring Boot启动流程及源码实现深度解析

Spring Boot启动流程及源码实现深度解析 一、启动流程概述 Spring Boot的启动流程围绕SpringApplication类展开&#xff0c;核心流程可分为以下几个阶段&#xff1a; 初始化阶段&#xff1a;推断应用类型&#xff0c;加载ApplicationContextInitializer和ApplicationListene…...

使用pip在Windows机器上安装Open Webui,配合Ollama调用本地大模型

之前的文章分享过在 linux 服务器上安装&#xff0c;并使用Open-webui 来实现从页面上访问本地大模型的访问。也写了文章分享了我在家里 Windows Server 台式机上安装 Ollama 部署本地大模型&#xff0c;并分别使用 Chatbox 和 CherryStudio 来访问本地的大模型。今天我来分享一…...

go map的声明和使用

1.简介 map是key-value数据结构&#xff0c;又称为字段或者关联数据。类似其他语言的集合&#xff0c;map在go中是引用类型&#xff0c;必须初始化才能使用。 2.语法 map[keytype]valuetype keytype:表示间的类型。可以是基本数据类型&#xff0c;还可以是指针、channl等。…...

word毕业论文“et al.”替换为“等”——宏

Sub 中文参考文献改等()中文参考文献改等 宏Selection.Find.ClearFormattingSelection.Find.Replacement.ClearFormattingWith Selection.Find.Text "([一-龥], )et al.".Replacement.Text "\1等.".Forward True.Wrap wdFindContinue.Format False.Ma…...

23. 观察者模式

原文地址: 观察者模式 更多内容请关注&#xff1a;智想天开 1. 观察者模式简介 观察者模式&#xff08;Observer Pattern&#xff09;是一种行为型设计模式&#xff0c;用于建立对象之间的一种一对多的依赖关系。当一个对象的状态发生变化时&#xff0c;所有依赖于它的对象都…...

go的”ambiguous import in multiple modules”

执行“go mod tidy”报如下错误&#xff1a; go mod tidy -compat1.17 go: finding module for package github.com/gomooon/goredis go: found github.com/gomooon/goredis in github.com/gomooon/goredis v0.3.5 go: github.com/gomooon/core importsgithub.com/gomooon/gor…...

【鸿蒙开发】MongoDB入门

https://www.mongodb.com/try/download/community 下载MongoDB: var mongoose require("mongoose");// localhost 域名&#xff0c;代表本机 // 127.0.0.1 ip , 代码本机 mongoose.connect("mongodb://localhost:27017/jiaju").then(() > {console.l…...

【应用篇】MLU上deepseek/QwQ-32B+dify实现workflow应用

文章目录 前言一、平台环境选择二、创建容器应用三、启动服务1.下载deepseekR1-14B模型2.VLLM启动服务3.postman测试服务 四、workflow搭建1.搭建第一个工作流2.详细配置 五、效果演示 前言 本章主要讲解如何用paas平台&#xff0c;实现智能体应用 本章中大模型我们使用deeps…...

vue组件库el-menu导航菜单设置index,地址不会变更的问题

请先确认 1.路由已配置好 route-index.js如下&#xff0c; 2.view-ProHome.vue中已预留路由展示位 3.导航菜单复制组件库&#xff0c;并做修改 其中index与路由配置的地址一致 运行后发现点击菜单&#xff0c;url地址还是不变&#xff0c;查看组件库 Element - The worlds …...

防抖和节流

防抖&#xff08;Debounce&#xff09;和节流&#xff08;Throttle&#xff09;是前端开发中常用的两种性能优化技术&#xff0c;主要用于控制高频事件的触发频率&#xff0c;避免不必要的性能消耗。 1. 防抖&#xff08;Debounce&#xff09; 防抖的核心思想&#xff1a;在事…...

Deepseek可以通过多种方式帮助CAD加速工作

自动化操作&#xff1a;通过Deepseek的AI能力&#xff0c;可以编写脚本来自动化重复性任务。例如&#xff0c;使用Python脚本调用Deepseek API&#xff0c;在CAD中实现自动化操作。 插件开发&#xff1a;结合Deepseek进行二次开发&#xff0c;可以创建自定义的CAD插件。例如&a…...

基于Spring Boot的宠物猫认养系统的设计与实现(LW+源码+讲解)

专注于大学生项目实战开发,讲解,毕业答疑辅导&#xff0c;欢迎高校老师/同行前辈交流合作✌。 技术范围&#xff1a;SpringBoot、Vue、SSM、HLMT、小程序、Jsp、PHP、Nodejs、Python、爬虫、数据可视化、安卓app、大数据、物联网、机器学习等设计与开发。 主要内容&#xff1a;…...

开源!速度100Kb/s的有线和无线双模ESP32S3芯片的DAP-Link调试器

开源&#xff01;速度100Kb/s的有线和无线双模ESP32S3芯片的DAP-Link调试器 目录 开源&#xff01;速度100Kb/s的有线和无线双模ESP32S3芯片的DAP-Link调试器本项目未经授权&#xff0c;禁止商用&#xff01;本项目未经授权&#xff0c;禁止商用&#xff01;本项目未经授权&…...

Vue3 模板引用:打破数据驱动的次元壁(附高阶玩法)

在数据驱动的Vue世界中&#xff0c;模板引用&#xff08;Template Refs&#xff09;是我们与真实DOM对话的秘密通道。本文将带你深入理解这个"逃生舱"的正确打开方式&#xff0c;并分享实战中的高阶技巧。 一、基础入门&#xff1a;建立DOM连接 1. 创建模板引用 &…...

第五天 Labview数据记录(5.5 SQL数据库读写)

5.5 SQL数据库读写 SQL 数据库读写操作是现代软件开发、数据分析和企业信息系统的核心功能。其意义不仅体现在技术层面&#xff0c;还涉及到业务流程优化、数据管理、决策支持等多个方面。以下是 SQL 数据库读写操作的重要意义&#xff1a;1. 数据存储与管理&#xff1b;2. 支…...

微信小程序项目引入图片问题:Error: module ‘assets/img/topImg.jpg.js‘ is not defined

问题与处理策略 问题描述 在微信小程序项目中&#xff0c;通过 require 引入图片文件&#xff0c;报如下错误 Error: module assets/img/topImg.jpg.js is not defined, require args is ../../assets/img/topImg.jpg# 翻译错误&#xff1a;未定义模块“assets/img/topImg.…...

02C#基本结构篇(D4_注释-访问修饰符-标识符-关键字-运算符-流程控制语句)

目录 一、注释 1. 单行注释 2. 多行注释 3. XML文档注释 4. 使用建议和最佳实践&#xff1a; 二、访问修饰符 1. public 2. private 3. protected 4. internal 5. protected internal 或 protected and internal 6. private protected 或 private and protected 7.…...

Python:正则表达式

正则表达式的基础和应用 一、正则表达式核心语法&#xff08;四大基石&#xff09; 1. ​元字符&#xff08;特殊符号&#xff09;​ ​定位符 ^&#xff1a;匹配字符串开始位置 $&#xff1a;匹配字符串结束位置 \b&#xff1a;匹配单词边界​&#xff08;如 \bword\b 匹配…...

ChatGPT4.5详细介绍和API调用详细教程

OpenAI在2月27日发布GPT-4.5的研究预览版——这是迄今为止OpenAI最强大、最出色的聊天模型。GPT-4.5在扩大预训练和微调规模方面迈出了重要的一步。通过扩大无监督学习的规模&#xff0c;GPT-4.5提升了识别内容中的模式、建立内容关联和生成对于内容的见解的能力&#xff0c;但…...

linux makefile tutorial

一个makefile的教程&#xff0c;几个小时就能看完&#xff0c;对makefile有个总体加细节的系统了解&#xff0c;非常不错&#xff1a; Learn Makefiles With the tastiest examples 中文翻译版&#xff1a; 起步 - Makefile 教程 (gavinliu6.github.io) gcc官网手册&#x…...

学习C2CRS Ⅲ (Response Generation Module)

代码地址:https://github.com/RUCAIBox/WSDM2022-C2CRS 论文地址:https://arxiv.org/abs/2201.02732 CFSelectionConvModel模型结构与功能 CFSelectionConvModel 是一个用于对话推荐系统的端到端模型,结合了知识图谱(KG)、评论信息和对话上下文来生成对话响应。它通过以…...

SpringBoot全栈开发:从数据库到Markdown文件导出的终极实践指南

一、SpringBoot后端核心实现 1.1 数据库数据转MD文件 通过SpringBoot实现数据库内容导出为Markdown文件&#xff0c;是文档自动化生成的关键技术&#xff1a; GetMapping("/download") public void exportMd(HttpServletResponse response, Integer id) {Content …...

go函数详解

1.简介 函数是组织好的、可重复使用的&#xff0c;用于执行指定任务的代码块&#xff0c;为了完成某一个功能的程序指令的集合&#xff0c;称为函数。go语言中支持&#xff1a;函数、匿名函数和闭包。 2.函数的定义 func 函数名 (形参列表) (返回值列表){ 函数体 return …...

MVCC实现原理

一、引言 在现代数据库管理系统中&#xff0c;数据的一致性和并发性是两个至关重要的特性。传统的锁机制虽然有效&#xff0c;但也存在着性能瓶颈&#xff0c;特别是在高并发环境下&#xff0c;锁的争用会导致系统响应时间变慢&#xff0c;甚至引发死锁等问题。为了克服这些挑…...

通过Golang的container/list实现LRU缓存算法

文章目录 力扣&#xff1a;146. LRU 缓存主要结构 List 和 Element常用方法1. 初始化链表2. 插入元素3. 删除元素4. 遍历链表5. 获取链表长度使用场景注意事项 源代码阅读 在 Go 语言中&#xff0c;container/list 包提供了一个双向链表的实现。链表是一种常见的数据结构&#…...