Elasticsearch基础3——密钥库工具、证书生成工具及四种生成模式、https请求步骤流程

一、密钥库工具(elasticsearch-keystore)

es密钥库官方文档

密钥库存在的意义?

  • es中的有些配置信息敏感需要保密,从而官方提供了密钥库工具elasticsearch-keystore功能 ,我们可以把想要保密的配置信息存放在密钥库里,提高安全维度。
    在这里插入图片描述

注意事项:

  1. 密钥库所有命令都适用于 Elasticsearch 用户。
  2. 对密钥库所做的所有修改,都必须在重新启动es服务之后才能生效。
  3. 当密钥存储库受密码保护时,您必须在每次Elasticsearch启动时提供密码。
  4. 若是es集群,则需要在每个节点上设置相同的值。

常规操作:

  1. 创建密钥库
  2. 查看密钥库中的设置列表
  3. 添加字符串设置
  4. 添加文件设置
  5. 删除密钥设置
  6. 可重新加载的安全设置等等

1.1 列出密钥库里的设置

命令格式:

  • ./bin/elasticsearch-keystore list

注意事项:

  • 若es密钥存储库受密码保护,则会提示输入密码。

在这里插入图片描述

  • elasticsearch.keystore 文件就是用来存放一些我们想要保密的 key/value 数据。

在这里插入图片描述

1.2 查看密钥库密码

命令格式:

  • ./bin/elasticsearch-keystore has-passwd

注意事项:

  • 以下可以看出我们的keystore 没有设置密码。

在这里插入图片描述

1.3 设置/改变秘钥库密码

命令格式:

  • ./bin/elasticsearch-keystore passwd

注意事项:

  • 以下可以看出,需要使用es用户才能对密钥库进行更改设置。
  • 输入空代表无密码。

1.设置密钥库密码为citms。若不输入任何东西,也就设置密码。
在这里插入图片描述
2.重启es服务时就需要我们输入刚才设置的密码。
在这里插入图片描述
3.此时,再次使用该命令,会先让我们输入原密钥,第二行是让我们输入密钥库的新密码(空为无密码)。我这里就是直接回车,代表无密码。再次重启es服务时,就不会再提示输入密钥库密码了。

在这里插入图片描述

1.4 添加字符串设置

命令格式:

  • ./bin/elasticsearch-keystore add 【设置项】
  • 如上命令就是添加云插件的身份验证凭据,回车后会让你设置值。

注意事项:

  • 若Elasticsearch密钥存储库受密码保护,还会提示输入密码。
  • 一次性添加多个字符串设置时,需使用换行符。
  • 我这里已经设置了环境变量,图方便,所以就是elasticsearch-keystore add the.setting.name.to.set,下文同理。

1.添加云插件身份凭证字符串设置,回车后会让我们设置值。
在这里插入图片描述
2.添加多个字符串设置时,使用“\”换行符号,回车后会依次让我们设置每个字符串设置的值。

在这里插入图片描述
3.也可以使用–stdin参数来标准输入传递设置值。

在这里插入图片描述

1.5 添加文件设置

命令格式:

  • ./bin/elasticsearch-keystore add-file 【设置项】 【文件路径】

注意事项:

  • 若Elasticsearch密钥存储库受密码保护,还会提示输入密码。
  • 一次性添加多个文件设置时,需使用换行符。

1.添加时,必须使用绝对路径。

在这里插入图片描述
2.添加多个文件时,需使用换行符。

在这里插入图片描述

1.6 移除密钥设置

命令格式:

  • ./bin/elasticsearch-keystore remove 【设置项】

注意事项:

  • 若Elasticsearch密钥存储库受密码保护,还会提示输入密码。
  • 一次性删除多个设置时,需使用换行符,这里不在列举。

在这里插入图片描述

1.6 升级密钥库

命令格式:

  • ./bin/elasticsearch-keystore upgrade

使用场景:

  • 偶尔,密钥存储库的内部格式会发生变化。当从包管理器安装Elasticsearch时,将在包升级期间将磁盘上的密钥存储库升级为新格式。
  • 在其他情况下,Elasticsearch在节点启动期间执行升级。这要求Elasticsearch对包含密钥库的目录具有写权限。这时可以使用upgrade命令手动升级。

在这里插入图片描述

1.7 创建密钥库

命令格式:

  • ./bin/elasticsearch-keystore create

注意事项:

  • 命令执行后,将创建2个文件,文件名分别为 elasticsearch.keystore 和elasticsearch.yml。
  • 现版本安装es后,会默认创建密钥库,所以也就是为什么会把这个命令放在最后讲。

1.重新创建会会提示是否覆盖文件,覆盖后密钥库的设置也呗覆盖。

在这里插入图片描述

1.8 参数大全

在这里插入图片描述

参数说明
add < settings >将设置添加秘钥存储库,可以将多个设置名作为add命令的参数。
默认情况下,系统会提示你输入设置值。
如果设置在秘钥库中已经存在,你必须确认想要覆盖当前值。
如果秘钥库不存在,你必须确认想要创建秘钥库。
要避免这两个确认提示,可以使用-f参数。
add-file (< setting > < path >)+将文件添加到秘钥存储库,可以同时将多个文件添加到秘钥存储库。
设置(< setting>)和路径(< path>)是成对指定的。路径必须是绝对路径,否则提示找不到文件。
create创建秘钥存储库。秘钥存储库文件elasticsearch.keystore与elasticsearch.yml在一起。
如果文件已存在会提示是否覆盖文件,如果同时使用-f标记,则会跳过确认直接覆盖已有文件。
list列出秘钥存储库中的设置。
passwd设置或改变秘钥存储库密码。可以通过输入空密码来移除原密码。
如果当前秘钥存储库受密码保护,会提示你输入当前密码和新的密码。
如果秘钥存储库不受密码保护,则可以使用该命令设置密码。
remove移除秘钥存储库中的设置。可以将多个设置名作为remove命令的参数。
如果删除不存在的设置会提示设置不存在。r
upgrade升级秘钥存储库的内部格式。需要Elasticsearch对包含keystore的目录具有写权限。
-f,–force当使用add命令时覆盖秘钥存储库中已存在的条目时,将不会进行提示。
如果没有创建秘钥存储库,则会创建一个经过混淆但不受密码保护的秘钥存储库。
-p当与create命令一起使用时,命令会提示你输入密码。
如果不指定-p标记或者输入空密码时,秘钥存储库会混淆处理但不受密码保护。
-s,–silent最小化输出。
-x,–stdin当与add命令一起使用时,可以通过标准输入(stdin)来传递设置值。用回车或换行符分隔多个值。
-v,–verbose最大化输出。
-h,–help返回所有命令参数。

二、证书生成工具(elasticsearch-certutil)

es证书生成官方文档
在这里插入图片描述

2.1 四种模式

哪四种模式?

  • 可以选择ca、cert、csr、http四种模式,每种模式都有不同的参数选项。
  • elasticsearch-certutil命令还支持静默操作模式,以支持更简单的批处理操作。

在这里插入图片描述

2.1.1 CA模式

CA模式作用:

  • CA模式生成新的证书颁发机构(ca)。默认情况下,它生成一个PKCS#12输出文件,其中保存CA证书和CA的私钥。
  • 也指定–pem参数,则命令生成一个zip文件,其中包含PEM格式的证书和私钥。随后可以使用这些文件作为命令的cert模式的输入。

PKCS#12是什么文件?

  • PKCS#12是一种交换数字证书的加密标准。通常用它来加密打包一个私钥及有关的 X.509 证书,产生的文件就是PKCS#12文件。

什么是CA证书?

  1. 证书,用来证明受访问的服务身份信息。
  2. 签名,存在证书上的一个可信标识,代表该证书是经过认证的,因为假冒服务器也可以有证书。
  3. CA证书,就是公认可靠的CA(certificate authority)机构签发的证书。
    在这里插入图片描述

https原理参考文章

HTTPS请求步骤流程:

  1. 浏览器发起https请求。
  2. 服务器返回它的证书。
  3. 浏览器通过CA的公钥对证书签名进行校验,检查证书是否有效。
  4. 浏览器生成一个临时秘钥并用服务器的公钥对它加密,然后将其发送给服务器。
  5. 服务器用私钥解密,得到浏览器发送给它的秘钥, 然后用该秘钥对数据进行加密
  6. 浏览器得到加密数据,并用发给服务端的秘钥进行解密。

在这里插入图片描述

1.生成ca证书时,依次会让我们输出证书名称、设置密码,两者也可以不设置直接回车使用默认。一旦设置密码,在后续的操作流程中需要保持一致。该文件是密文

[es-qingjun@localhost certs]$ elasticsearch-certutil ca

在这里插入图片描述
2.使用–pem参数,则生成的是一个zip压缩包,解压后会创建一个ca目录,里面存放的是一对公钥和私钥,明文。

[es-qingjun@localhost certs]$ elasticsearch-certutil ca --pem

在这里插入图片描述
在这里插入图片描述

2.1.2 CERT模式

CERT模式说明:

  • 该证书模式生成X.509证书和私钥。默认情况下,它生成一个证书和密钥,用于单个实例。
  • –multiple参数:为多个实例生成证书和密钥,该参数会提示我们取提供每个实例的详细信息。
  • –n参数:指定包含实例洋细信息的YAML文件。
  • –pem参数,该命令将生成PEM格式的证书和密钥,并将它们打包到zip文件中,解压后是明文。

模式作用:

  • 在es中,常常用于对CA证书的验证。

注意事项:

  • 实例是弹性堆栈中需要TLS或SSL证书的任何部分。elk中的ElasticSearch、Logstash、Kibana和Beats都会用到证书和私钥。
  • 实例所需的最小信息是其名称,该名称用作证书的通用名称。实例名可以是主机名值,也可以是完整的distinquished名称。
  • 若实例名导致无效的文件名或目录名,则还必须在–name命令参数中或在输入YAML文件的filename字段中指定文件名。
  • 默认情况下,证书模式生成单个PKCS#12输出文件,该文件包含实例证书、实例私钥和CA证书。如果指定–multiple或–n参数,该命令将生成一个zip文件。

1.验证上一步生成的CA证书,输入那个证书的密码,再依次输入生成的第二个CA证书名称、
密码。

[es-qingjun@localhost elasticsearch-8.5.2]$ elasticsearch-certutil cert --ca elastic-stack-ca.p12

在这里插入图片描述
2.使用–pem参数,则生成zip压缩文件,解压出来时明文。

[es-qingjun@localhost elasticsearch-8.5.2]$ elasticsearch-certutil cert --pem --ca elastic-stack-ca.p12

在这里插入图片描述
在这里插入图片描述
3.使用–multiple参数,则生成zip压缩文件,解压出来是密文。

[es-qingjun@localhost elasticsearch-8.5.2]$ elasticsearch-certutil cert --multiple --ca elastic-stack-ca.p12

在这里插入图片描述

2.1.3 CSR模式

CSR模式说明:

  • CSR模式生成证书签名请求(CSR),我们可以将这些请求发送给受信任的证书颁发机构以获取已签名的证书。默认情况下,该命令为单个实例生成单个CSR。
  • 签名的证书必须是PEM或PKCS#12格式才能使用Elasticsearch安全特性。
  • –multiple参数:为多个实例生成CSR,该参数会提示我们提供每个实例的详细信息。
  • –in参数:指定包含实例详细信息的YAML文件。
    CSR模式产生一个包含CSR和每个实例的私钥的zip文件。每个CSR都是作为PKCS#10CSR的标准PEM编码提供的。每个密钥都是作为RSA私钥的PEM编码提供的。

1.生成csr证书请求。

[es-qingjun@localhost elasticsearch-8.5.2]$ elasticsearch-certutil csr

在这里插入图片描述
2.解压文件后,目录包含证书签名请求和私钥,提供证明向证书颁发机构签名请求。
在这里插入图片描述

2.1.4 HTTP模式

HTTP模式说明:

  • http模式指导您完成生成证书的过程,以便在Elasticsearch的HTTP(REST)接口上使用。它会问您一些问题,以便根据您的需要生成一组正确的文件。
  • 该种模式,流程长且杂,需要多练习才能熟悉。
[es-qingjun@localhost elasticsearch-8.5.2]$ elasticsearch-certutil http

2.1.5 相关参数

参数释义注意事项
ca指定生成新的本地证书颁发机构(CA)。此参数不能与csr或cert参数一起使用。
cert指定生成新的X.509证书和密钥。此参数不能与csr或ca参数一起使用。
csr指定生成证书签名请求。此参数不能与ca或cert参数一起使用。
http为Elasticsearch HTTP接口生成一个新的证书或证书请求。
–ca <file_path>指定到现有CA密钥对的路径(采用PKCS#12格式)。此参数不能与ca或csr参数一起使用。
–ca-cert <file_path>指定现有CA证书的路径(PEM格式),还必须指定–ca-key参数。该–ca-cert 参数不能与ca或csr参数一起使用。
–ca-dn 用于生成的CA证书的专有名称(DN)。默认值为 CN=Elastic Certificate Tool Autogenerated CA。该参数不能与csr参数一起使用。
–ca-key <file_path>指定现有CA私钥的路径(PEM格式),还必须指定–ca-cert参数。该–ca-key 参数不能与ca或csr参数一起使用。
–ca-pass 指定现有CA私钥或生成的CA私钥的密码。此参数不能与ca或 csr参数一起使用。
–days 指定一个整数值,该整数值表示生成的证书有效的天数。默认值为1095。该参数不能与csr参数一起使用。
–dns <domain_name>指定逗号分隔的DNS名称列表。该参数不能与ca参数一起使用。
-E 配置设置。
-h, --help返回所有命令参数。
–in <input_file>指定用于以静默方式运行的文件。输入文件必须是YAML文件。该参数不能与ca 参数一起使用。
–ip <IP_addresses>指定逗号分隔的IP地址列表。该参数不能与ca参数一起使用。
–keep-ca-key在cert具有自动生成的CA的模式下运行时,指定保留CA私钥以备将来使用。
–keysize 定义在生成的RSA密钥中使用的位数。默认值为2048。
–multiple指定为多个实例生成文件。该参数不能与ca参数一起使用。
–name <file_name>指定生成的证书的名称。该参数不能与ca参数一起使用。
–out <file_path>指定输出文件的路径。
–pass 指定生成的私钥的密码。以PKCS#12格式存储的密钥始终受密码保护,但此密码可能为空。
若要在没有提示的情况下指定空白密码,在命令行上使用–pass “”(不带=)。
仅在–pass指定参数的情况下,以PEM格式存储的密钥才受密码保护 。
如果不为参数提供 --pass参数,则提示输入密码。
加密的PEM文件不支持空白密码(如果不希望使用密码保护PEM密钥,则不要指定 --pass)。
–pem以PEM格式而不是PKCS#12生成证书和密钥。该参数不能与csr参数一起使用。
–self-signed生成自签名证书。该参数只适用于cert参数。(慎用)
不建议在集群上设置TLS。
事实上,当只有确信绝对不需要CA并且直接对证书本身给予信任时,才应该使用自签名证书。
-s, --silent显示最少的输出。
-v, --verbose显示详细输出。

2.1.6 练习

2.1.6.1 练习一

1.生成CA证书。

[es-qingjun@localhost elasticsearch-8.5.2]$ elasticsearch-certutil ca

在这里插入图片描述

2.1.6.2 练习二

1.使用刚刚生成的CA证书,生成X.509证书和私钥,并指定输出路径文件为config/certs/qingjun(密文),不设置密码,且以最简输出。

[es-qingjun@localhost elasticsearch-8.5.2]$ elasticsearch-certutil cert --ca elastic-stack-ca.p12 --ca-pass "" --out config/certs/qingjun --pass "" -s

在这里插入图片描述

2.1.6.3 练习三
  • 使用静默模式输出时,需要先编辑一个yml文件。
  • yml文件模板
instances:
  - name: "node1"   ##实例的名称。这可以是一个简单的字符串值,也可以是一个专有名称(DN)。这是唯一必填字段。
    ip:       ##代表此实例的IP地址的字符串的可选数组。IPv4和IPv6值均允许。这些值将作为主题备用名称添加。
      - "192.0.2.1"
    dns:      ##代表此实例的DNS名称的可选字符串数组。这些值将作为主题备用名称添加。
      - "node1.mydomain.com"
  - name: "node2"
    ip:
      - "192.0.2.2"
      - "198.51.100.1"
  - name: "node3"
  - name: "node4"
    dns:
      - "node4.mydomain.com"
      - "node4.internal"
  - name: "CN=node5,OU=IT,DC=mydomain,DC=com"
    filename: "node5"   ##用于此实例的文件名。该名称用作在输出中包含实例文件的目录的名称。它也用在目录中文件的名称中。该文件名不应具有扩展名。注意:如果name为实例提供的名称不代表有效的文件名,则该filename字段必须存在。

1.先编辑yml文件

[es-qingjun@localhost elasticsearch-8.5.2]$ cat config/qingjun.yml 
instances:
  - name: "node1" 
    ip: 
      - "192.168.130.140"
    filename: "node1"

2.使用刚刚生成的CA证书,生成X.509证书和私钥。–ca-pass指定CA证书的密码;–in指定yml文件位置;–out指定输出文件名称;–pass指定生成的X.509证书和私钥密码;-s最简输出(静默模式)。

[es-qingjun@localhost elasticsearch-8.5.2]$ elasticsearch-certutil cert --ca elastic-stack-ca.p12  --ca-pass ""  --in config/qingjun.yml  --out test1.zip --pass "" -s

3.解压生成的test1.zip压缩包,里面就是X.509证书和私钥。
在这里插入图片描述

2.2 生成CA证书

1.生成CA证书。

[es-qingjun@localhost elasticsearch-8.5.2]$ elasticsearch-certutil ca

2.对生成的证书进行验证。此时一共会有三个文件:elastic-certificates.p12、elasticsearch.keystore、elastic-stack-ca.p12。

[es-qingjun@localhost elasticsearch-8.5.2]$ elasticsearch-certutil cert --ca elastic-stack-ca.p12

#以下两步输入第一步创建CA证书时设置的密码。
[es-qingjun@localhost elasticsearch-8.5.2]$ elasticsearch-keystore add xpack.security.transport.ssl.keystore.secure_password 
[es-qingjun@localhost elasticsearch-8.5.2]$ elasticsearch-keystore add xpack.security.transport.ssl.truststore.secure_password

3.将elastic-certificates.p12、elastic-stack-ca.p12两个文件移入cong/cert目录下。
在这里插入图片描述
4.修改es主配置文件,因为默认不是这个配置名称。
在这里插入图片描述
5.最后重启es服务即可。

  • 2
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
好的,我可以为您提供相关的信息和代码示例。首先,您需要确保您的Spring Boot项目中包含以下依赖项: ```xml <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-elasticsearch</artifactId> </dependency> <dependency> <groupId>org.elasticsearch.client</groupId> <artifactId>elasticsearch-rest-high-level-client</artifactId> <version>7.0.0</version> </dependency> ``` 接下来,您需要在application.properties文件中添加以下配置: ```properties spring.data.elasticsearch.cluster-name=my-application spring.data.elasticsearch.cluster-nodes=localhost:9300 ``` 然后,您可以编写Elasticsearch工具类来执行一些常见的操作,例如索引文档、搜索文档等。以下是一个示例工具类: ```java import org.elasticsearch.action.ActionListener; import org.elasticsearch.action.bulk.BulkRequest; import org.elasticsearch.action.bulk.BulkResponse; import org.elasticsearch.action.get.GetRequest; import org.elasticsearch.action.index.IndexRequest; import org.elasticsearch.action.search.SearchRequest; import org.elasticsearch.action.search.SearchResponse; import org.elasticsearch.action.update.UpdateRequest; import org.elasticsearch.client.RequestOptions; import org.elasticsearch.client.RestHighLevelClient; import org.elasticsearch.common.unit.TimeValue; import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentFactory; import org.elasticsearch.index.query.QueryBuilders; import org.elasticsearch.search.SearchHits; import org.elasticsearch.search.builder.SearchSourceBuilder; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import java.io.IOException; import java.util.Map; @Component public class ElasticsearchUtil { @Autowired private RestHighLevelClient restHighLevelClient; /** * 索引文档 * @param indexName 索引名称 * @param id 文档ID * @param sourceMap 文档内容 * @throws IOException */ public void indexDocument(String indexName, String id, Map<String, Object> sourceMap) throws IOException { IndexRequest request = new IndexRequest(indexName); if (id != null) { request.id(id); } request.source(sourceMap); restHighLevelClient.index(request, RequestOptions.DEFAULT); } /** * 更新文档 * @param indexName 索引名称 * @param id 文档ID * @param sourceMap 文档内容 * @throws IOException */ public void updateDocument(String indexName, String id, Map<String, Object> sourceMap) throws IOException { UpdateRequest request = new UpdateRequest(indexName, id); request.doc(sourceMap); restHighLevelClient.update(request, RequestOptions.DEFAULT); } /** * 批量索引文档 * @param indexName 索引名称 * @param sourceMaps 文档内容列表 * @throws IOException */ public void bulkIndexDocuments(String indexName, Map<String, Object>[] sourceMaps) throws IOException { BulkRequest request = new BulkRequest(); for (Map<String, Object> sourceMap : sourceMaps) { IndexRequest indexRequest = new IndexRequest(indexName); indexRequest.source(sourceMap); request.add(indexRequest); } restHighLevelClient.bulk(request, RequestOptions.DEFAULT); } /** * 批量更新文档 * @param indexName 索引名称 * @param idList 文档ID列表 * @param sourceMap 文档内容 * @throws IOException */ public void bulkUpdateDocuments(String indexName, String[] idList, Map<String, Object> sourceMap) throws IOException { BulkRequest request = new BulkRequest(); for (String id : idList) { UpdateRequest updateRequest = new UpdateRequest(indexName, id); updateRequest.doc(sourceMap); request.add(updateRequest); } restHighLevelClient.bulk(request, RequestOptions.DEFAULT); } /** * 获取文档 * @param indexName 索引名称 * @param id 文档ID * @return * @throws IOException */ public Map<String, Object> getDocument(String indexName, String id) throws IOException { GetRequest request = new GetRequest(indexName, id); return restHighLevelClient.get(request, RequestOptions.DEFAULT).getSourceAsMap(); } /** * 搜索文档 * @param indexName 索引名称 * @param keyword 关键词 * @param size 每页数量 * @param from 起始位置 * @return * @throws IOException */ public SearchHits searchDocuments(String indexName, String keyword, int size, int from) throws IOException { SearchRequest searchRequest = new SearchRequest(indexName); SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder(); searchSourceBuilder.query(QueryBuilders.matchQuery("content", keyword)); searchSourceBuilder.from(from); searchSourceBuilder.size(size); searchSourceBuilder.timeout(TimeValue.timeValueSeconds(60)); searchRequest.source(searchSourceBuilder); SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT); return searchResponse.getHits(); } } ``` 最后,您可以在您的代码中使用ElasticsearchUtil类,并根据您的需求调用它的方法来执行所需的操作。以下是一个示例调用: ```java import org.elasticsearch.search.SearchHits; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import java.io.IOException; import java.util.HashMap; import java.util.Map; @RestController public class DemoController { @Autowired private ElasticsearchUtil elasticsearchUtil; @GetMapping("/index") public String index() throws IOException { Map<String, Object> sourceMap = new HashMap<>(); sourceMap.put("title", "Elasticsearch 7 教程"); sourceMap.put("content", "Elasticsearch 7 入门教程"); elasticsearchUtil.indexDocument("my_index", null, sourceMap); return "索引文档成功!"; } @GetMapping("/search") public String search(@RequestParam("keyword") String keyword, @RequestParam("size") int size, @RequestParam("from") int from) throws IOException { SearchHits searchHits = elasticsearchUtil.searchDocuments("my_index", keyword, size, from); return searchHits.toString(); } } ``` 希望这些代码示例可以帮助您整合Elasticsearch 7到您的Spring Boot项目中,并提供了一些常见的操作示例。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

百慕卿君

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值