ACME 协议是一种开放标准,旨在实现数字证书颁发和续订流程的自动化,它彻底改变了证书管理。ACME 的开发旨在简化整个流程,已被许多证书颁发机构 (CA) 广泛采用,并已成为互联网标准 (RFC 8555).

为了充分理解ACME协议考虑如何让代理更方便使用ACME协议申请TLS/SSL证书,本人照着LettuceEncryptcertes 从0实现了一遍 ACME协议client以及如何在asp.net core 中集成ACME申请管理。

本文接下来会详细说明相关内容。

ACME协议内容

这里简要描述一下协议内容,方便大家理解,详细还是得看协议原文。

一个简要ACME协议申请流程大致如下

client  -- 0. 申请账号 -->                      ACME服务器
| <-- 账号信息 -- | | -- 1. 创建证书申请订单 --> |
<-- 订单信息 -- | -- 2. 选择http/dns/tls中任意之一验证方式 --> |
<-- 返回验证信息 | -- 3. 部署验证信息
<---| | -- 4. 告知可以进行验证行为 |
异步进行验证,成功则标明订单可以生成证书 --|
| --> | -- 4.1 client 可轮询api 确认验证结果 | -- 5.验证通过提交CSR最终确定订单 --> | | -- 6. 下载证书 --> |

不同验证行为如下

  • http

    这是当今最常见的验证方式。 ACME服务器 如Let’s Encrypt 向您的 ACME 客户端提供一个令牌,然后您的 ACME 客户端将在您对 Web 服务器的 http://<你的域名>/.well-known/acme-challenge/(用提供的令牌替换 )路径上放置指定文件。 该文件包含令牌以及帐户密钥的指纹。 一旦您的 ACME 客户端告诉 ACME服务器 如Let’s Encrypt 文件已准备就绪,ACME服务器 如Let’s Encrypt 会尝试获取它(可能从多个地点进行多次尝试)。 如果我们的验证机制在您的 Web 服务器上找到了放置于正确地点的正确文件,则该验证被视为成功,您可以继续申请颁发证书。 如果验证检查失败,您将不得不再次使用新证书重新申请。

    即 需要提供 GET http://{申请域名}/.well-known/acme-challenge/{验证Token} api ,并返回 {验证Token}.{AccountKey.Thumbprint()}

    ACME服务器会确认返回是否一致

    优点:

    • 它可以轻松地自动化进行而不需要关于域名配置的额外知识。
    • 它允许托管服务提供商为通过 CNAME 指向它们的域名颁发证书。
    • 它适用于现成的 Web 服务器。
    • 它也可以用于验证 IP 地址。

    缺点:

    • 如果您的 ISP 封锁了 80 端口,该验证将无法正常工作(这种情况很少见,但一些住宅 ISP 会这么做)。
    • ACME服务器 如Let’s Encrypt 不允许您使用此验证方式来颁发通配符证书。
    • 您如果有多个 Web 服务器,则必须确保该文件在所有这些服务器上都可用。
  • dns

    此验证方式要求您在该域名下的 TXT 记录中放置特定值来证明您控制域名的 DNS 系统。它允许您颁发通配符证书。 在 ACME服务器 如Let’s Encrypt 为您的 ACME 客户端提供令牌后,您的客户端将创建从该令牌和您的帐户密钥派生的 TXT 记录,并将该记录放在 _acme-challenge.<YOUR_DOMAIN> 下。 然后 ACME服务器 如Let’s Encrypt 将向 DNS 系统查询该记录。 如果找到匹配项,您就可以继续颁发证书!

    优点:

    • 您可以使用此验证方式来颁发包含通配符域名的证书。
    • 即使您有多个 Web 服务器,它也能正常工作。
    • 即使服务器不对公网开放,您也可以通过此方式验证其域名。

    缺点:

    • 在 Web 服务器上保留 API 凭据存在风险。
    • 您的 DNS 提供商可能不提供 API。
    • 您的 DNS API 可能无法提供有关更新时间的信息。
    • IP 地址不能通过此方式验证。
    • vkproxy lib 默认不提供dns自动验证实现,因为DNS 提供商太多了,api 不统一,且可能不提供 API
  • tls

    通过 443 端口上的 TLS 执行。 但是,它使用自定义的 ALPN 协议来确保只有知道此验证类型的服务器才会响应验证请求。 这还允许对此质询类型的验证请求使用与要验证的域名匹配的SNI字段,从而使其更安全。

    这一验证类型并不适合大多数人。 它最适合那些想要执行类似于 HTTP-01 的基于主机的验证,但希望它完全在 TLS 层进行以分离关注点的 TLS 反向代理的作者。

    优点:

    • 它在 80 端口不可用时仍可以正常工作。
    • 它可以完全仅在 TLS 层执行。
    • 它也可以用于验证 IP 地址。

    缺点:

    • 它不支持 Apache、Nginx 和 Certbot,且很可能短期内不会兼容这些软件。
    • 与 HTTP-01 一样,如果您有多台服务器,则它们需要使用相同的内容进行应答。
    • 此方法不能用于验证通配符域名。

pebble 本地测试的acme服务

由于过于贫穷,所以无法在真实的域名/acme服务商/dns服务商等等进行真实的示例

不过好在 letsencrypt.org 开源提供了可以在本地测试的acme服务 pebble

配置示例

{
"pebble": {
"listenAddress": "0.0.0.0:14000",
"managementListenAddress": "0.0.0.0:15000",
"certificate": "D:\\Program Files\\tool\\pebble\\certs\\localhost\\cert.pem",
"privateKey": "D:\\Program Files\\tool\\pebble\\certs\\localhost\\key.pem",
"httpPort": 80,
"tlsPort": 443,
"ocspResponderURL": "",
"externalAccountBindingRequired": false,
"domainBlocklist": ["blocked-domain.example"],
"retryAfter": {
"authz": 3,
"order": 5
},
"profiles": {
"default": {
"description": "The profile you know and love",
"validityPeriod": 7776000
},
"shortlived": {
"description": "A short-lived cert profile, without actual enforcement",
"validityPeriod": 518400
}
}
}
}

启动命令

.\pebble.exe -config .\pebble-config.json

命令行

安装命令行

dotnet tool install --global VKProxy.Cli
// 测试ACME服务访问协议
vkproxy acme terms --dangerous-certificate true --timeout 00:10:00 --server https://127.0.0.1:14000/dir // output:
// data:text/plain,Do what thou wilt
// 生成pem格式账号密钥到 accountkey 文件,acme协议默认是通过密钥关联账号
vkproxy acme account key --algorithm ES256 --output accountkey --format pem // accountkey 文件内容:
// -----BEGIN EC PRIVATE KEY-----
// MHcCAQEEIKAVlrieijZYRLawRZhNCTfQU++umiQD1TcFCv1POgQboAoGCCqGSM49
// AwEHoUQDQgAEPhWnyoiS01MdguO4NA/4RmXO0GEFAg7a9F08KwjILDZPNNNy8XTj
// WyUU6j4IZUbt/SM53QJXNEEYqdfDU53jag==
// -----END EC PRIVATE KEY-----
// 新建account账号
vkproxy acme account new --contact mailto:test@t.org --key accountkey --dangerous-certificate true --timeout 00:10:00 --server https://127.0.0.1:14000/dir // output:
// Location: https://127.0.0.1:14000/my-account/359a2c1419c73551
// Account: {"status":"valid","contact":["mailto:test@t.org"],"orders":"https://127.0.0.1:14000/list-orderz/359a2c1419c73551"}
// 新建订单
vkproxy acme order new --domains kubernetes.docker.internal --key accountkey --dangerous-certificate true --timeout 00:10:00 --server https://127.0.0.1:14000/dir // output:
// https://127.0.0.1:14000/my-order/KP92OBeu8Loim2er5K_ugWLtjGsdntMMzL28mDPhbmA
// {"status":"pending","expires":"2025-07-27T07:09:34+00:00","identifiers":[{"type":"dns","value":"kubernetes.docker.internal"}],"authorizations":["https://127.0.0.1:14000/authZ/BmLiNkioTcxg1KCmJrHYiapFyDZAUo87w0wP8WwQYP8"],"finalize":"https://127.0.0.1:14000/finalize-order/KP92OBeu8Loim2er5K_ugWLtjGsdntMMzL28mDPhbmA"}
// 使用 http 方式验证
vkproxy acme order authz --domain kubernetes.docker.internal --order https://127.0.0.1:14000/my-order/KP92OBeu8Loim2er5K_ugWLtjGsdntMMzL28mDPhbmA --challenge-type http --key accountkey --dangerous-certificate true --timeout 00:10:00 --server https://127.0.0.1:14000/dir // output:
// {"location":"https://127.0.0.1:14000/chalZ/t0NRkPk5MiJuc5TQkaf6u9fAZzLV4K9Kwy0ApvrhULs","challengeUri":".well-known/acme-challenge/aHBa1xzb32c_VvS5Lsi8s6pIB7JeyqHxIQ4C490jDH0","challengeTxt":"aHBa1xzb32c_VvS5Lsi8s6pIB7JeyqHxIQ4C490jDH0.z1pFvCHE8G1C_w6FrgJqy-YK2cUpLAFgtFzMx4bKsjg","resource":{"type":"http-01","url":"https://127.0.0.1:14000/chalZ/t0NRkPk5MiJuc5TQkaf6u9fAZzLV4K9Kwy0ApvrhULs","status":"pending","token":"aHBa1xzb32c_VvS5Lsi8s6pIB7JeyqHxIQ4C490jDH0"}} // 这里就要求我们部署一个处理challengeUri 的api `GET http://kubernetes.docker.internal/.well-known/acme-challenge/aHBa1xzb32c_VvS5Lsi8s6pIB7JeyqHxIQ4C490jDH0` 返回 challengeTxt `aHBa1xzb32c_VvS5Lsi8s6pIB7JeyqHxIQ4C490jDH0.z1pFvCHE8G1C_w6FrgJqy-YK2cUpLAFgtFzMx4bKsjg`
// 比如 部署一个 asp.net core 程序, 它包含如下内容
// app.Map("/.well-known/acme-challenge", mapped =>
// {
// mapped.Use(async (HttpContext c, Func<Task> next) =>
// {
// string value = "aHBa1xzb32c_VvS5Lsi8s6pIB7JeyqHxIQ4C490jDH0.z1pFvCHE8G1C_w6FrgJqy-YK2cUpLAFgtFzMx4bKsjg";
// c.Response.ContentLength = value?.Length ?? 0;
// c.Response.ContentType = "application/octet-stream";
// await c.Response.WriteAsync(value);
// await c.Response.CompleteAsync();
// });
// });
// 部署好服务后,告知acme验证
vkproxy acme order validate --domain kubernetes.docker.internal --order https://127.0.0.1:14000/my-order/KP92OBeu8Loim2er5K_ugWLtjGsdntMMzL28mDPhbmA --challenge-type http --key accountkey --dangerous-certificate true --timeout 00:10:00 --server https://127.0.0.1:14000/dir
// output:
// {"location":"https://127.0.0.1:14000/chalZ/t0NRkPk5MiJuc5TQkaf6u9fAZzLV4K9Kwy0ApvrhULs","resource":{"result":{"type":"http-01","url":"https://127.0.0.1:14000/chalZ/t0NRkPk5MiJuc5TQkaf6u9fAZzLV4K9Kwy0ApvrhULs","status":"processing","token":"aHBa1xzb32c_VvS5Lsi8s6pIB7JeyqHxIQ4C490jDH0"},"id":1,"status":"ranToCompletion","isCanceled":false,"isCompleted":true,"isCompletedSuccessfully":true,"creationOptions":"none","isFaulted":false}}
// 这里可以看到 "status":"processing"
// 通过list查看
vkproxy acme order list --key accountkey --dangerous-certificate true --timeout 00:10:00 --server https://127.0.0.1:14000/dir // output:
// https://127.0.0.1:14000/my-order/KP92OBeu8Loim2er5K_ugWLtjGsdntMMzL28mDPhbmA
// {"status":"ready","expires":"2025-07-27T07:09:34+00:00","identifiers":[{"type":"dns","value":"kubernetes.docker.internal"}],"authorizations":["https://127.0.0.1:14000/authZ/BmLiNkioTcxg1KCmJrHYiapFyDZAUo87w0wP8WwQYP8"],"finalize":"https://127.0.0.1:14000/finalize-order/KP92OBeu8Loim2er5K_ugWLtjGsdntMMzL28mDPhbmA"}
// 这里可以看到 "status":"ready" 说明验证成功,证书可以下载了
// 下载证书,因为 pebble是本地测试服务,无合法根证书,所以要多添加 --additional-issuer issuer.txt ,issuer.txt内容来自 pebble 服务 https://127.0.0.1:15000/roots/0 

vkproxy acme order finalize --algorithm ES256 --format pem --output cert --additional-issuer issuer.txt --domain kubernetes.docker.internal --order https://127.0.0.1:14000/my-order/KP92OBeu8Loim2er5K_ugWLtjGsdntMMzL28mDPhbmA --challenge-type http --key accountkey --dangerous-certificate true --timeout 00:10:00 --server https://127.0.0.1:14000/dir

// output:
// cert.pem
// -----BEGIN CERTIFICATE-----
// MIICYDCCAUigAwIBAgIIT4B4lP9vtcQwDQYJKoZIhvcNAQELBQAwKDEmMCQGA1UE
// AxMdUGViYmxlIEludGVybWVkaWF0ZSBDQSA0NzA1OTUwHhcNMjUwNzI2MDczMzQ2
// WhcNMjUwODAxMDczMzQ1WjAAMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAExpcZ
// fAZJgCeZ6iXBWmGUvwzq+RqmtUQG8jO2JEpIzTPmBHWQdLvWBiCrZQ5ssF64e44D
// UbiVbMExvpX5GIUNDaOBgDB+MA4GA1UdDwEB/wQEAwIHgDATBgNVHSUEDDAKBggr
// BgEFBQcDATAMBgNVHRMBAf8EAjAAMB8GA1UdIwQYMBaAFOAW+aaPdbX8v+N58YWB
// w5Umg3luMCgGA1UdEQEB/wQeMByCGmt1YmVybmV0ZXMuZG9ja2VyLmludGVybmFs
// MA0GCSqGSIb3DQEBCwUAA4IBAQBWylL5NhRQzJ/m7n7GUhyKEM0jvybH5uNkRu9V
// NR2hQdz/rPc8bw+9N3z3iNHkn65V9W6iC9xlwXXD7jAiYmtf3LLhYvkenbfJA72d
// f8j5brIM+3IYAnLCMkkyIsFfSMfj9pwrPt/qMjkxFq2QmoFsgPQgx/xImU+OiKKP
// t1lWaAMP/qiNWWbtRSyZ51C3RGNIVH0q+JoSRVgkbRXoxWueQted3YkBV8VDbbIW
// o0Jk6Y6xBeNFx1Lz5yqa3xnotE9m7VFTxlkaHLRkGDoO0dgj+3FHK+0XLoNt8jgN
// b9RgzCxAIBxkAlvx5VJOpApFTJhXR6hvDwyKmVvyXbZbx/7A
// -----END CERTIFICATE-----
// -----BEGIN PUBLIC KEY-----
// MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAExpcZfAZJgCeZ6iXBWmGUvwzq+Rqm
// tUQG8jO2JEpIzTPmBHWQdLvWBiCrZQ5ssF64e44DUbiVbMExvpX5GIUNDQ==
// -----END PUBLIC KEY-----
// -----BEGIN PRIVATE KEY-----
// MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQg6efLajrFYCnWy4i3
// YW5Mc1L1h04oWat/786OQEh+1JihRANCAATGlxl8BkmAJ5nqJcFaYZS/DOr5Gqa1
// RAbyM7YkSkjNM+YEdZB0u9YGIKtlDmywXrh7jgNRuJVswTG+lfkYhQ0N
// -----END PRIVATE KEY-----
这就是证书所有内容了

asp.net core 中使用

只需配置好 acme 相关设置即可启动, 如

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers();
builder.Services.AddOpenApi(); builder.Services.AddAcmeChallenge(o =>
{
o.AllowedChallengeTypes = VKProxy.ACME.AspNetCore.ChallengeType.Http01;
o.RenewDaysInAdvance = TimeSpan.FromDays(2);
o.Server = new Uri("https://127.0.0.1:14000/dir");
o.DomainNames = new[] { "kubernetes.docker.internal" };
o.NewAccount(new string[] { "mailto:test@xxx.com" });
o.AdditionalIssuers = new[] {"""
-----BEGIN CERTIFICATE-----
MIIDGzCCAgOgAwIBAgIIU3M7k6+spYMwDQYJKoZIhvcNAQELBQAwIDEeMBwGA1UE
AxMVUGViYmxlIFJvb3QgQ0EgMDYyYzdjMCAXDTI1MDcyNjA3MDA1MVoYDzIwNTUw
NzI2MDcwMDUxWjAgMR4wHAYDVQQDExVQZWJibGUgUm9vdCBDQSAwNjJjN2MwggEi
MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDZKIeNyaVFwuVOSc+3Q3bSznnf
QLDtUHnpwwzY6VaCW5x/M+zK4ykrIUJvC8qE55TL7YmBnJ5uT0DDsjLoZAWudRGS
UKvivcoEWectl2YfhUSCqw1LbTuK52UQTWNNwfe+1rmFPs2C3yyfEA78221SsQsj
FfbTZkhLgDpajtSLs9yZy+wEael8xvdMAO+REm9I8sCoK31DEs3ZNQBcrSDyT9mz
URhzDRahov7bg2MJmBxZH8ICfINd1yZA9kNghLtaRSRLF3JZWcjCr4H1MdjlJFDY
pQzfa7ZHCHW1fzwdRvi/zjASKvYAkr+arweQSYIqKrs9wN+ah09uEhztOz59AgMB
AAGjVzBVMA4GA1UdDwEB/wQEAwIChDATBgNVHSUEDDAKBggrBgEFBQcDATAPBgNV
HRMBAf8EBTADAQH/MB0GA1UdDgQWBBSH6Q9bP8CGt5JpTCMMNZj4j/DiqDANBgkq
hkiG9w0BAQsFAAOCAQEAryVZdW8KihxLrh4yRuLbIXpjyWacoblvUrWwIQ5vnwwt
RDoo0mHlYVOxo0ueiUQ4vi5kkGZk7VEsDXi6GV+KT/maupq6Hr+o6drKDO8iYA33
XuDCNOgfPOXusmiPJFCm07Ah+yV3BxLWMl3azbuiGIWyRZI+fzdnGD1Rh1vPXtI8
3JgSyqOrNLBQUVMfdhEAYNZrlFBuqUbxXEvA24IL2UgNpYTwAn2iYCcg2zpw5E/c
DtjJTHO5x+uyXsaRQDXkJ9OZbeil691JcJH7TNxAJVe5N46JFdIf7ELvyJek/K5/
xted2WWSLd/WQ2UPxxdfceRE1IDH0X88kk/OmmzujA==
-----END CERTIFICATE----- """
};
}, c =>
{
c.HttpClientConfig = new VKProxy.Config.HttpClientConfig()
{
DangerousAcceptAnyServerCertificate = true
};
}); var app = builder.Build(); app.UseAuthorization(); app.MapControllers(); app.Run();

打开debug log, 可以看到相关申请证书的log

info: VKProxy.ACME.AspNetCore.AcmeState[0]
Using account https://127.0.0.1:14000/my-account/43cc0ec8ef818d32
dbug: VKProxy.ACME.AspNetCore.AcmeState[0]
Creating new order for a certificate
dbug: VKProxy.ACME.AspNetCore.AcmeState[0]
Validate Http01 for kubernetes.docker.internal
dbug: VKProxy.ACME.AspNetCore.AcmeState[0]
GetAuthorization kubernetes.docker.internal
dbug: VKProxy.ACME.AspNetCore.AcmeState[0]
GetAuthorization kubernetes.docker.internal
dbug: VKProxy.ACME.AspNetCore.HttpChallengeResponseMiddleware[0]
Confirmed challenge request for vnEH5-HvtTiFvkygIBO6Njmbu6YY7-l9DXyrCwwmSMU
dbug: VKProxy.ACME.AspNetCore.HttpChallengeResponseMiddleware[0]
Confirmed challenge request for vnEH5-HvtTiFvkygIBO6Njmbu6YY7-l9DXyrCwwmSMU
dbug: VKProxy.ACME.AspNetCore.HttpChallengeResponseMiddleware[0]
Confirmed challenge request for vnEH5-HvtTiFvkygIBO6Njmbu6YY7-l9DXyrCwwmSMU
dbug: VKProxy.ACME.AspNetCore.AcmeState[0]
GetAuthorization kubernetes.docker.internal
dbug: VKProxy.ACME.AspNetCore.AcmeState[0]
Creating cert for kubernetes.docker.internal
warn: VKProxy.ACME.AspNetCore.ServerCertificateSelector[0]
Failed to validate certificate for (AD6C43DE80E1E3FB8975F0FC2EE2E545FC42DD10). This could cause an outage of your app.
dbug: VKProxy.ACME.AspNetCore.AcmeState[0]
Checking certificates' renewals for kubernetes.docker.internal

证书已经被加载到 asp.net core 中, 所以https 请求将会看到使用的 pebble的证书

Certificate CN

Issuer CN

Pebble Intermediate CA 470595

比如请求

curl --location 'https://localhost:443/WeatherForecast' \
--header 'Host: kubernetes.docker.internal'

不过在 asp.net core 这样使用证书,个人并不推荐,这种方式存在一些问题

  • 实例需要访问ACME 服务,存在额外网络维护和安全的成本
  • ACME 服务通常存在一些限流,以避免攻击或滥用,当实例很多或反复启动容易产生问题
  • 这样使用就会导致同一域名存在很多证书,一旦某一实例无法更新证书,实例就会产生问题,人工处理可能比较麻烦

合理做法可以是有单独程序提供证书管理的功能,证书更新则可以在变更后由管理程序调用 代理程序api进行更新。

后面有空会尝试一下

VKProxy 是使用c#开发的基于 Kestrel 实现 L4/L7的代理(感兴趣的同学烦请点个github小赞赞呢)

c# ACME client的更多相关文章

  1. 免费SSL证书 之Let’s Encrypt申请与部署(Windows Nginx)

    我着着皇帝的新衣,但是你看不见    有一颗愿意等待的心,说明你对未来充满希望.有一颗充满希望的心,那么等待又算什么.人就是在等待与希望中度过,我们永远要对未来充满信心! 读在最前面: 1.本文案例为 ...

  2. 从 HTTP 到 HTTPS - IIS 部署免费 HTTPS

    这篇文章首发于我的个人网站:听说 - https://tasaid.com/,建议在我的个人网站阅读,拥有更好的阅读体验. 这篇文章与 博客园 和 Segmentfault 共享. 前端开发QQ群:3 ...

  3. Awesome Go

    A curated list of awesome Go frameworks, libraries and software. Inspired by awesome-python. Contrib ...

  4. Spring Boot+CXF搭建WebService(转)

    概述 最近项目用到在Spring boot下搭建WebService服务,对Java语言下的WebService了解甚少,而今抽个时间查阅资料整理下Spring Boot结合CXF打架WebServi ...

  5. Lets encrypt安装及配置

    letsencrypt recommend that most people with shell access use the Certbot ACME client.It can automate ...

  6. 生成线上用https证书,支持通配符和多域名,初学Let’s Encrypt用于IIS,纯本地手动

    自简书发布的上篇<生成本地测试用https证书,支持通配符和多域名,初学OpenSSL>以来,本地测试用https用的妥妥的. 线上一直用的腾讯云的免费证书(每个域名都要一个证书(滑稽), ...

  7. 使用lets encrypt获取免费ssl证书

    lets encrypt也是一个CA,并且在众多大厂的加持下有可能成为最棒的免费颁发证书的CA,尤其是chrome的加入. 目前https已经成为了一种趋势,无奈证书授权费用相当昂贵,将一大批企业挡在 ...

  8. Go 语言相关的优秀框架,库及软件列表

    If you see a package or project here that is no longer maintained or is not a good fit, please submi ...

  9. HTTPS证书申请相关笔记

    申请免费的HTTPS证书相关资料 参考资料: HTTPS 检测 苹果ATS检测 什么是ECC证书? 渠道2: Let's Encrypt 优点 缺点 Let's Encrypt 的是否支持非80,44 ...

  10. windows server使用 LetsEncrypt-Win-Simple来安装和使用用Let's Encrypt免费SSL证书

    一.网站部署 LetsEncrypt-Win-Simple可以自动发现已经部署的网站供我们选择要生成证书的网站,而且还需要进行验证.所以在生成证书之前,确保网站已经部署好并可以正常访问. 二.生成证书 ...

随机推荐

  1. MySQL中create_time 和 update_time实现自动更新时间

    也是最近在捣鼓前后端分离项目, 在写后端接口的时候便设计到数据库表建设, 这里规范显得很重要. 通常的建表规范, 必备三字段:id,create_time,update_time. id 必为主键,类 ...

  2. DevEco重大更新快来体验吧

    HarmonyOS首个api17 release已经正式发布了.这次的更新DevEco增加了很多重大特性,系统能力也有所增强.DevEco现在支持创建api17的应用了,模拟器也首次支持阔折叠手机以及 ...

  3. TVM:TensorIR

    TensorIR是一种用于深度学习的特定领域语言,主要有两个目的. 在各种硬件后端进行程序变换和优化的实现 用于自动张量化程序优化的抽象 import tvm from tvm.script.pars ...

  4. 部署Spring Boot项目详细教程

    首先Spring Boot项目能正常使用IP地址搭配接口在浏览器正常运行 第一步: 打开Maven里面Lifecycle下面的package或者是install双击运行(需要有网络) 第二步: 查看运 ...

  5. 【转载】Refletor源码分析

    Refletor源码分析 Informer 通过对 APIServer 的资源对象执行 List 和 Watch 操作,把获取到的数据存储在本地的缓存中,其中实现这个的核心功能就是 Reflector ...

  6. Springboot笔记<5>静态资源访问

    静态资源访问 静态资源目录 请求进来,先去找Controller看能不能处理.不能处理的所有请求又都交给静态资源处理器.静态资源也找不到则响应404页面.如果静态目录中存在a.png,访问localh ...

  7. Golang指针解析

    一.简单说明 golang指针可以这样理解:本身为一个整型常量,但由于其声明时为指针,因此拥有了特殊的能力,即在其前增加 * ,即可直接访问内存编号为该整型常量的数据.而对于某个定义的常量,在前面加 ...

  8. 纯C#软实现openGL(V0.1),黑盒变白盒

    纯C#软实现openGL(V0.1),黑盒变白盒 为了彻底掌握openGL,做一个openGL的软实现(命名为SoftGLImpl)是必要的.(而非仅仅调用opengl32.dll) openGL A ...

  9. Vertx 实现webapi实战项目(一)

    关于Vertx的介绍在官方文档可以查看 https://vertx-china.github.io/docs/.网上也有许多介绍,这里就直接上项目. 一:使用idea搭建gradle项目.添加相关ve ...

  10. Django+DRF 实战:从异常捕获到自定义错误信息

    一.DRF 异常 介绍 APIException 类是 DRF 异常的基类.通过继承APIException,DRF 提供了多种内置异常类. ParseError:当请求解析失败时抛出. Authen ...