openssl 生成多域名 多IP 的数字证书
openssl.cnf 文件内容:
[req] default_bits = 2048 distinguished_name = req_distinguished_name copy_extensions = copy req_extensions = req_ext x509_extensions = v3_req prompt = no [req_distinguished_name] countryName = CN stateOrProvinceName = GuangDong localityName = ShenZhen organizationName = lc commonName = CA [req_ext] basicConstraints = CA:FALSE subjectAltName = @alt_names [v3_req] basicConstraints = CA:FALSE subjectAltName = @alt_names [alt_names] IP.1 = 192.168.10.31 IP.2 = 192.168.10.32 IP.3 = 192.168.10.33 DNS.1 = 192.168.10.2 DNS.2 = 202.96.134.133
生成证书
工具是用的:windows平台 Win64OpenSSL-3_2_0.exe 或 Win64OpenSSL_Light-3_2_0.exe (建议用:Win64OpenSSL-3_2_0.exe )
OpenSSL 3.2.0 23 Nov 2023 (Library: OpenSSL 3.2.0 23 Nov 2023)
根证书: openssl genrsa -out ca.key 2048 openssl req -x509 -new -nodes -key ca.key -sha256 -days 3650 -out ca.pem -subj "/C=CN/ST=GuangDong/O=EMQX/CN=Client" 服务端证书: openssl genrsa -out emqx.key 2048 openssl req -new -key emqx.key -config openssl.cnf -out emqx.csr openssl x509 -req -in emqx.csr -CA ca.pem -CAkey ca.key -CAcreateserial -out emqx.pem -days 3650 -sha256 -extensions v3_req -extfile openssl.cnf 客户端证书: openssl genrsa -out client.key 2048 openssl req -new -key client.key -out client.csr -subj "/C=CN/ST=GuangDong/O=EMQX/CN=Client" openssl x509 -req -days 3650 -in client.csr -CA ca.pem -CAkey ca.key -CAcreateserial -out client.pem 校验证书的有效性: openssl verify -CAfile ca.pem emqx.pem openssl verify -CAfile ca.pem client.pem
常见错误:
Error [ERR_TLS_CERT_ALTNAME_INVALID]: Hostname/IP does not match certificate's altnames: IP: 192.168.10.32 is not in the cert's list:
Error: self signed certificate in certificate chain
Error: Connection refused: Not authorized # 没有设置用户名密码
Error: unable to verify the first certificate
加密认证算法:
package com.lc.common.mqtt.utils;import org.bouncycastle.asn1.pkcs.PrivateKeyInfo; import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.bouncycastle.openssl.PEMParser; import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter; import org.springframework.core.io.ResourceLoader; import org.springframework.stereotype.Component; import java.io.*; import java.security.KeyStore; import java.security.PrivateKey; import java.security.Security; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; import javax.net.ssl.KeyManagerFactory; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLSocketFactory; import javax.net.ssl.TrustManagerFactory;/*** @author Charley* @date 2022/12/05* @description*/ @Component public class SSLUtils {@javax.annotation.Resourceprivate ResourceLoader resourceLoader;public SSLSocketFactory getSingleSocketFactory(InputStream caCrtFileInputStream) throws Exception {Security.addProvider(new BouncyCastleProvider());X509Certificate caCert = null;BufferedInputStream bis = new BufferedInputStream(caCrtFileInputStream);CertificateFactory cf = CertificateFactory.getInstance("X.509");while (bis.available() > 0) {caCert = (X509Certificate) cf.generateCertificate(bis);}KeyStore caKs = KeyStore.getInstance(KeyStore.getDefaultType());caKs.load(null, null);caKs.setCertificateEntry("cert-certificate", caCert);TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());tmf.init(caKs);SSLContext sslContext = SSLContext.getInstance("TLSv1.2");sslContext.init(null, tmf.getTrustManagers(), null);return sslContext.getSocketFactory();}public SSLSocketFactory getSocketFactory(final String caCrtFile,final String crtFile, final String keyFile, final String password)throws Exception {Security.addProvider(new BouncyCastleProvider());// load CA certificateX509Certificate caCert = null;// FileInputStream fis = new FileInputStream(caCrtFile);BufferedInputStream bis = new BufferedInputStream(resourceLoader.getResource(caCrtFile).getInputStream());CertificateFactory cf = CertificateFactory.getInstance("X.509");while (bis.available() > 0) {caCert = (X509Certificate) cf.generateCertificate(bis);}// load client certificate//bis = new BufferedInputStream(new FileInputStream(crtFile));bis = new BufferedInputStream(resourceLoader.getResource(crtFile).getInputStream());X509Certificate cert = null;while (bis.available() > 0) {cert = (X509Certificate) cf.generateCertificate(bis);}// load client private key // PEMParser pemParser = new PEMParser(new FileReader(keyFile)); // Object object = pemParser.readObject(); // JcaPEMKeyConverter converter = new JcaPEMKeyConverter().setProvider("BC"); // KeyPair key = converter.getKeyPair((PEMKeyPair) object); // pemParser.close();// PEMParser pemParser =new PEMParser(new InputStreamReader(new FileInputStream(keyFile)));PEMParser pemParser =new PEMParser(new InputStreamReader(resourceLoader.getResource(keyFile).getInputStream()));Object obj = pemParser.readObject();JcaPEMKeyConverter converter = new JcaPEMKeyConverter();PrivateKey privateKey = converter.getPrivateKey((PrivateKeyInfo) obj);// CA certificate is used to authenticate serverKeyStore caKs = KeyStore.getInstance(KeyStore.getDefaultType());caKs.load(null, null);caKs.setCertificateEntry("ca-certificate", caCert);TrustManagerFactory tmf = TrustManagerFactory.getInstance("X509");tmf.init(caKs);// client key and certificates are sent to server, so it can authenticateKeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());ks.load(null, null);ks.setCertificateEntry("certificate", cert);ks.setKeyEntry("private-key", privateKey, password.toCharArray(),new java.security.cert.Certificate[]{cert});KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());kmf.init(ks, password.toCharArray());// finally, create SSL socket factorySSLContext context = SSLContext.getInstance("TLSv1.2");context.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);return context.getSocketFactory();} }
mqq5:
package com.lc.common.mqtt.mqttv5;import cn.hutool.core.util.IdUtil; import com.lc.common.mqtt.config.MqttConfig; import com.lc.common.mqtt.utils.SSLUtils; import lombok.extern.slf4j.Slf4j; import org.eclipse.paho.mqttv5.client.MqttConnectionOptions; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.integration.annotation.ServiceActivator; import org.springframework.integration.channel.DirectChannel; import org.springframework.integration.core.MessageProducer; import org.springframework.integration.mqtt.outbound.Mqttv5PahoMessageHandler; import org.springframework.integration.mqtt.support.MqttHeaderMapper; import org.springframework.messaging.MessageChannel; import org.springframework.messaging.MessageHandler; import javax.annotation.Resource;@Configuration @Slf4j public class Mqtt5Client {@ResourceMqttConfig mc;@Resourceprivate SSLUtils sslUtils;@Resourceprivate Mqtt5MessageReceiver mqttMessageReceiver;/*** (生产者) mqtt消息出站通道,用于发送出站消息* @return*/@Beanpublic MessageChannel mqttOutputChannel5() {return new DirectChannel();}/*** (消费者) mqtt消息入站通道,订阅消息后消息进入的通道。* @return*/@Beanpublic MessageChannel mqttInputChannel5() {return new DirectChannel();}public MqttConnectionOptions getOptions() {MqttConnectionOptions options = new MqttConnectionOptions();options.setServerURIs(mc.getServices());options.setUserName(mc.getUser());options.setPassword(mc.getPassword().getBytes());options.setReceiveMaximum(mc.getMaxInflight());options.setKeepAliveInterval(mc.getKeepAliveInterval());// 重连设置options.setAutomaticReconnect(mc.isAutomaticReconnect());options.setMaxReconnectDelay(mc.getMaxReconnectDelay());options.setAutomaticReconnectDelay(mc.getV5AutomaticReconnectMinDelay(), mc.getV5AutomaticReconnectMaxDelay());// 会话设置options.setCleanStart(mc.isV5CleanStart());options.setSessionExpiryInterval(mc.getV5SessionExpiryInterval());// 超时设置options.setConnectionTimeout(mc.getConnectionTimeout());try {options.setSocketFactory(sslUtils.getSocketFactory("classpath:ca.pem","classpath:client.pem","classpath:client.key",""));} catch (Exception e) {e.printStackTrace();}return options;}/*** 生产者* @return*/@Bean@ServiceActivator(inputChannel = "mqttOutputChannel5")public MessageHandler mqttOutbound5() {String clientId = mc.getV5ProducerId() + "_" + IdUtil.getSnowflakeNextId();;Mqttv5PahoMessageHandler messageHandler = new Mqttv5PahoMessageHandler(getOptions(), clientId);messageHandler.setHeaderMapper(new MqttHeaderMapper());// 设置异步不阻塞messageHandler.setAsync(false);// 设置QosmessageHandler.setDefaultQos(mc.getQos());return messageHandler;}/*** MQTT消息订阅绑定(消费者)* @return*/@Beanpublic MessageProducer channelInbound5(MessageChannel mqttInputChannel5) {String clientId = mc.getV5ConsumerId() + "_" + IdUtil.getSnowflakeNextId();;MyMqttv5PahoMessageDrivenChannelAdapter adapter = new MyMqttv5PahoMessageDrivenChannelAdapter(getOptions(), clientId, mc.getV5DefaultTopic());adapter.setCompletionTimeout(mc.getCompletionTimeout());adapter.setPayloadType(String.class);adapter.setQos(mc.getQos());adapter.setOutputChannel(mqttInputChannel5);return adapter;}/*** MQTT消息处理器(消费者)* @return*/@Bean@ServiceActivator(inputChannel = "mqttInputChannel5")public MessageHandler mqttMessageHandler5() {return mqttMessageReceiver;} }
mqtt3
package com.lc.common.mqtt.mqttv3;import cn.hutool.core.util.IdUtil; import com.lc.common.mqtt.utils.SSLUtils; import lombok.extern.slf4j.Slf4j; import org.eclipse.paho.client.mqttv3.MqttConnectOptions; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.integration.annotation.ServiceActivator; import org.springframework.integration.channel.DirectChannel; import org.springframework.integration.core.MessageProducer; import org.springframework.integration.mqtt.core.DefaultMqttPahoClientFactory; import org.springframework.integration.mqtt.inbound.MqttPahoMessageDrivenChannelAdapter; import org.springframework.integration.mqtt.outbound.MqttPahoMessageHandler; import org.springframework.integration.mqtt.support.DefaultPahoMessageConverter; import org.springframework.messaging.MessageChannel; import org.springframework.messaging.MessageHandler; import com.lc.common.mqtt.config.MqttConfig; import javax.annotation.Resource;@Configuration @Slf4j public class Mqtt3Client {@Resourceprivate MqttConfig mc;@Resourceprivate SSLUtils sslUtils;@Resourceprivate Mqtt3MessageReceiver mqttMessageReceiver;/*** (生产者) mqtt消息出站通道,用于发送出站消息* @return*/@Beanpublic MessageChannel mqttOutputChannel3() {return new DirectChannel();}/*** (消费者) mqtt消息入站通道,订阅消息后消息进入的通道。* @return*/@Beanpublic MessageChannel mqttInputChannel3() {return new DirectChannel();}public MqttConnectOptions getOptions() {MqttConnectOptions options = new MqttConnectOptions();options.setServerURIs(mc.getServices());options.setUserName(mc.getUser());options.setPassword(mc.getPassword().toCharArray());options.setMaxInflight(mc.getMaxInflight());options.setKeepAliveInterval(mc.getKeepAliveInterval());// 重连设置options.setAutomaticReconnect(mc.isAutomaticReconnect());options.setMaxReconnectDelay(mc.getMaxReconnectDelay());// options.setAutomaticReconnectDelay(automaticReconnectMinDelay, automaticReconnectMaxDelay);// 会话设置options.setCleanSession(mc.isV3CleanSession());// 超时设置options.setConnectionTimeout(mc.getConnectionTimeout());// 设置遗嘱消息 qos 默认为 1 retained 默认为 falseoptions.setWill("willTopic","与服务器断开连接".getBytes(),0,false);try {options.setSocketFactory(sslUtils.getSocketFactory("classpath:ca.pem","classpath:client.pem","classpath:client.key",""));} catch (Exception e) {e.printStackTrace();}return options;}/*** 生产者* @return*/@Bean@ServiceActivator(inputChannel = "mqttOutputChannel3")public MessageHandler mqttOutbound3() {String clientId = mc.getV3ProducerId() + "_" + IdUtil.getSnowflakeNextId();DefaultMqttPahoClientFactory factory = new DefaultMqttPahoClientFactory() ;factory.setConnectionOptions(getOptions());MqttPahoMessageHandler messageHandler = new MqttPahoMessageHandler(clientId, factory);// 设置异步不阻塞messageHandler.setAsync(true);// 设置QosmessageHandler.setDefaultQos(mc.getQos());return messageHandler;}/*** MQTT消息订阅绑定(消费者)* @return*/@Beanpublic MessageProducer channelInbound3(MessageChannel mqttInputChannel3) {String clientId = mc.getV3ConsumerId() + "_" + IdUtil.getSnowflakeNextId();;DefaultMqttPahoClientFactory factory = new DefaultMqttPahoClientFactory();factory.setConnectionOptions(getOptions());MqttPahoMessageDrivenChannelAdapter adapter = new MqttPahoMessageDrivenChannelAdapter(clientId, factory, mc.getV3DefaultTopic());adapter.setCompletionTimeout(mc.getCompletionTimeout());adapter.setRecoveryInterval(mc.getV3RecoveryInterval());adapter.setConverter(new DefaultPahoMessageConverter());adapter.setQos(mc.getQos());adapter.setOutputChannel(mqttInputChannel3);return adapter;}/*** MQTT消息处理器(消费者)* @return*/@Bean@ServiceActivator(inputChannel = "mqttInputChannel3")public MessageHandler mqttMessageHandler3() {return mqttMessageReceiver;} }
0
0
« 上一篇: SSL/TSL 总结
» 下一篇: npm 错误,ERESOLVE unable to resolve dependency tree 解决方案
相关文章:

openssl 生成多域名 多IP 的数字证书
openssl.cnf 文件内容: [req] default_bits 2048 distinguished_name req_distinguished_name copy_extensions copy req_extensions req_ext x509_extensions v3_req prompt no [req_distinguished_name] countryName CN stateOrProvinceName GuangDong l…...

电影评论|基于springBoot的电影评论网站设计与实现(附项目源码+论文+数据库)
私信或留言即免费送开题报告和任务书(可指定任意题目) 目录 一、摘要 二、相关技术 三、系统设计 四、数据库设计 五、核心代码 六、论文参考 七、源码获取: 一、摘要 随着信息技术在管理上越来越深入而广泛的应用,…...

【C++】虚函数
一、什么是虚函数 在类的成员函数前加上virtual关键字,这个函数就是虚函数。 虚函数的所用就是完成多态。多态示例如下: class A {public:virtual void func()//虚函数{cout << "A" << endl;}void ftwo()//普通函数{cout <&…...

esxi虚拟机启用cbt备份(增量备份)
在虚拟机中启用CBT 1.关闭虚拟机。 右键点按虚拟机,Edit Settings-VM Options-Advanced-Configuration Parameters-Edit Configuration- Add parameters,添加ctkEnabled参数,并将其值设置为true。 Add parameters,添加scsi0:0…...

mysql 8.0 时间维度表生成(可运行)
文章目录 mysql 8.0 时间维度表生成实例时间维度表的作用时间维度表生成技术细节使用时间维度表的好处 mysql 8.0 时间维度表生成实例 时间维度表的作用 dim_times(时间维度表)在数据仓库(Data Warehouse)中的作用至关重要。作为…...

打造高效实时数仓,从Hive到OceanBase的经验分享
本文作者:Coolmoon1202,大数据高级工程师,专注于高性能软件架构设计 我们的业务主要围绕出行领域,鉴于初期采用的数据仓库方案面临高延迟、低效率等挑战,我们踏上了探索新数仓解决方案的征途。本文分享了我们在方案筛选…...
15.3 JDBC数据库编程
15.3 JDBC数据库编程 15.3.1 创建数据库和表 创建一个名为webstore的数据库,并向其中添加数据,代码如下: 1.创建数据库 CREATE TABLE products( id int PRIMARY KEY, pname VARCHAR(20) brand VARCHAR(20), price FLOAT(7,2), stock SMALLINT, ) …...

SSH公私钥后门从入门到应急响应
目录 1. SSH公私钥与SSH公私钥后门介绍 1.1 SSH公私钥介绍 1.1.1 公钥和私钥的基本概念 1.1.2 SSH公私钥认证的工作原理(很重要) 1.2 SSH公私钥后门介绍 2. 如何在已拿下控制权限的主机创建后门 2.1 使用 Xshell 生成公钥与私钥 2.2 将公钥上传到被需要被植入后门的服务…...

服务器数据恢复—Linux操作系统环境下网站数据的恢复案例
服务器数据恢复环境: 一台linux操作系统服务器上跑了几十个网站,服务器上只有一块SATA硬盘。 服务器故障: 服务器突然宕机,尝试再次启动失败。将硬盘拆下检测,发现存在坏扇区。找当地一家数据恢复公司处理后ÿ…...

开放式耳机是怎么样的?开放式耳机的优缺点分析?
开放式耳机作为一种独特的耳机类型,因其独特设计和使用体验受到了许多用户的喜爱。了解开放式耳机的优缺点有助于大家更好地选择适合自己需求的耳机。以下是开放式耳机的一些主要优点和缺点分析: 优点 l 舒适度高 开放式耳机的设计通常更加轻盈&#…...

HDMI色块移动——FPGA学习笔记13
一、方块移动原理 二、实验任务 使用FPGA开发板上的HDMI接口在显示器上显示一个不停移动的方块,要求方块移动到边界处时能够改变移动方向。显示分辨率为800*480,刷新速率为90hz。(480p分辨率为800*480,像素时钟频率Vga_clk 800x4…...

MySQL中去除重复
除去相同的行 SELECT DISTINCT 列名 FROM 表名; 示例:查询employees表,显示唯一的部门ID select distinct department_id from employees;...

【C++】vector容器的基本使用
一、vector是什么 vector是STL第一个正式的容器,它的底层其实就是动态数组,插入数据时当容量满了会自动扩容,它和string差不多,不同的之处之一在于vector本身是一个模板,它这个容器中可以存放各种各样的类型的数据&am…...

【强化学习系列】Gym库使用——创建自己的强化学习环境2:拆解官方标准模型源码/规范自定义类+打包自定义环境
目录 一、 官方标准环境的获取与理解 二、根据官方环境源码修改自定义 1.初始化__init__() 2.重置环境 reset() 三、打包环境 1.注册与创建自定义环境 2.环境规范化 在本文的早些时候,曾尝试按照自己的想法搭建自定义的基于gym强化学习环境。 【强化学习系列】Gy…...
PyQt5实现按钮选择文件夹及文件夹
目录 1、选择文件夹并显示 2、选择文件 3、选择多个文件 4、设置保存文件路径 1、选择文件夹并显示 from PyQt5 import QtWidgetsdirectory QtWidgets.QFileDialog.getExistingDirectory(None, "选取文件夹", "./") # 起始路径 print(directory) 2…...

Gin渲染
HTML渲染 【示例1】 首先定义一个存放模板文件的 templates文件夹,然后在其内部按照业务分别定义一个 posts 文件夹和一个 users 文件夹。 posts/index.tmpl {{define "posts/index.tmpl"}} <!DOCTYPE html> <html lang"en">&…...
前端——JS基础
定义变量:let / var 字符串 字符串拼接: 字符串和数字拼:您.... 25 ; 这个25会转成字符串再拼接 字符串和数组拼:10以内的质数有: [2,3,5,7] > 10以内的质数有:2,3,5,7 字符串长度:leng…...

MATLAB入门教程
MATLAB安装教程可参考链接:matlab怎么安装 matlab安装教程-电脑软件-PHP中文网 1.MATLAB的工作环境 (1)命令窗(command window) 是对MATLAB进行操作的主要载体。默认情况下,启动MATLAB时就打开命令窗。MATLAB的所有所数…...

muduo - 概要简述
作者:陈硕 编程语言:C 架构模式:Reactor 代码链接:GitHub - chenshuo/muduo: Event-driven network library for multi-threaded Linux server in C11 设计自述:https://www.cnblogs.com/Solstice/archive/2010/08…...
Selenium点击元素的方法
前言 点击方法在web自动化测试中经常用到,下面就来介绍一下selenium常用和不常用的点击方法; 1、常用方法 1.1、使用 click() 方法: 这是最简单和最常用的方法。通过选中要点击的元素,然后使用 click() 方法来触发点击事件。 示例代码: element = self.driver.find_e…...
浅谈 React Hooks
React Hooks 是 React 16.8 引入的一组 API,用于在函数组件中使用 state 和其他 React 特性(例如生命周期方法、context 等)。Hooks 通过简洁的函数接口,解决了状态与 UI 的高度解耦,通过函数式编程范式实现更灵活 Rea…...

23-Oracle 23 ai 区块链表(Blockchain Table)
小伙伴有没有在金融强合规的领域中遇见,必须要保持数据不可变,管理员都无法修改和留痕的要求。比如医疗的电子病历中,影像检查检验结果不可篡改行的,药品追溯过程中数据只可插入无法删除的特性需求;登录日志、修改日志…...

UDP(Echoserver)
网络命令 Ping 命令 检测网络是否连通 使用方法: ping -c 次数 网址ping -c 3 www.baidu.comnetstat 命令 netstat 是一个用来查看网络状态的重要工具. 语法:netstat [选项] 功能:查看网络状态 常用选项: n 拒绝显示别名&#…...

高频面试之3Zookeeper
高频面试之3Zookeeper 文章目录 高频面试之3Zookeeper3.1 常用命令3.2 选举机制3.3 Zookeeper符合法则中哪两个?3.4 Zookeeper脑裂3.5 Zookeeper用来干嘛了 3.1 常用命令 ls、get、create、delete、deleteall3.2 选举机制 半数机制(过半机制࿰…...
Matlab | matlab常用命令总结
常用命令 一、 基础操作与环境二、 矩阵与数组操作(核心)三、 绘图与可视化四、 编程与控制流五、 符号计算 (Symbolic Math Toolbox)六、 文件与数据 I/O七、 常用函数类别重要提示这是一份 MATLAB 常用命令和功能的总结,涵盖了基础操作、矩阵运算、绘图、编程和文件处理等…...
Unit 1 深度强化学习简介
Deep RL Course ——Unit 1 Introduction 从理论和实践层面深入学习深度强化学习。学会使用知名的深度强化学习库,例如 Stable Baselines3、RL Baselines3 Zoo、Sample Factory 和 CleanRL。在独特的环境中训练智能体,比如 SnowballFight、Huggy the Do…...

网络编程(UDP编程)
思维导图 UDP基础编程(单播) 1.流程图 服务器:短信的接收方 创建套接字 (socket)-----------------------------------------》有手机指定网络信息-----------------------------------------------》有号码绑定套接字 (bind)--------------…...

OPenCV CUDA模块图像处理-----对图像执行 均值漂移滤波(Mean Shift Filtering)函数meanShiftFiltering()
操作系统:ubuntu22.04 OpenCV版本:OpenCV4.9 IDE:Visual Studio Code 编程语言:C11 算法描述 在 GPU 上对图像执行 均值漂移滤波(Mean Shift Filtering),用于图像分割或平滑处理。 该函数将输入图像中的…...
Java + Spring Boot + Mybatis 实现批量插入
在 Java 中使用 Spring Boot 和 MyBatis 实现批量插入可以通过以下步骤完成。这里提供两种常用方法:使用 MyBatis 的 <foreach> 标签和批处理模式(ExecutorType.BATCH)。 方法一:使用 XML 的 <foreach> 标签ÿ…...

保姆级教程:在无网络无显卡的Windows电脑的vscode本地部署deepseek
文章目录 1 前言2 部署流程2.1 准备工作2.2 Ollama2.2.1 使用有网络的电脑下载Ollama2.2.2 安装Ollama(有网络的电脑)2.2.3 安装Ollama(无网络的电脑)2.2.4 安装验证2.2.5 修改大模型安装位置2.2.6 下载Deepseek模型 2.3 将deepse…...