怎样学做网站,创新的常州做网站,农博园网站建设投标书,wordpress后台不好用用Delphi调用阿里云的OpenAPI更新动态域名解析记录 家里一直是电信的宽带#xff0c;虽然只是200M下行30M上行的平均水平#xff0c;但是对于平时下载量不大的我来说已经绰绰有余了#xff0c;很多时候需要从外网访问家里的NAS的资料#xff0c;但是因为不是固定IP#xf…用Delphi调用阿里云的OpenAPI更新动态域名解析记录 家里一直是电信的宽带虽然只是200M下行30M上行的平均水平但是对于平时下载量不大的我来说已经绰绰有余了很多时候需要从外网访问家里的NAS的资料但是因为不是固定IP每次一重启路由器或者每隔几天家里的IP地址都是变动一下前阵子一直使用docker安装了一个阿里云的DDNS软件非常好用突然有一天不更新IP到阿里云的服务器上了又加上自己的管理软件的服务端是安装到Windows下用unRaidNAS虚拟了一个WindowsServer所以干脆想办法把这个功能加到自己的管理服务端软件上这时候开始祭出我们的工具Delphi。前几篇文章已经说了如何安装社区版的Delphi和如何在unRaid下虚拟Windows。 在正式操作之前首先说明下使用aliyun的DDNS的基本条件。 1、这个也是最最关键的必不可少的一步就是你家必须要有公网IP现在很多地方宽带供应商只给内网IP特别是移动和联通如果是电信的话可以打10000号免费开通公网IP功能至于给不给你开看各地的政策。反正我家一直是公网IP。 2、你得有个阿里云的域名如果没有赶紧去阿里云上去买个不差钱的可以买个.com、.cn的如果只是玩玩的可以买个.xyz等超级便宜的域名。至于腾讯云的域名有两种方法你可以把域名改为阿里云托管也可以将下面的代码稍微改动改成腾讯云的。因为我只有阿里云的域名所以没法测试腾讯云的这里就不改动了 3、获得阿里云的访问key和访问key的密钥。这个在你登录阿里云后在头像上点击鼠标左键会弹出来一个菜单就可以直接看到。这里也给个建议最好不要用主key和密钥因为一旦泄露也就是将你的阿里云的所有权限暴露给别人了所以可以建立一个子用户key并且给他设置权限只能修改域名解析。 4、提前在阿里云的域名管理上建立一个新的二级域名虽然可以通过代码添加但是我觉得自己手工添加会更安全至少不会让自己的域名列表不经意的多出很多无用的二级域名。 有了前面的几步现在正是进入主题。代码写起来比较简单只要根据阿里云的OpenAPI的文档编写就可以当然你得学会看懂文档。很快我就将所有的代码完成但是运行的时候一直提示错误最主要错误就是一直提示签名值错误这个签名值在我的理解也就是校验值用来校验你是否有权限访问和修改域名解析。然后慢慢调试在查看文档和阿里云公开的代码发现有几个大坑而在文档上却没有说明当然也可能和每个编程工具的处理有点小关系现在将每个大坑一一的说明下 坑1做签名值的时候发现调试出来的签名值很长几乎是官方计算出来的两到三倍代码仔细看看没什么打问题已经按照文档要求使用了URL编码后再进行HMAC_SHA1然后最后再进行BASE64编码整个过程都没任何问题而且编码函数经过测试也没问题。文档上也没有任何说明要做其他的编码或者转变。后来发现惊醒HMAC_SHA1编码的时候一定要生成他的原始二进制数据而我们正常情况下一般都是生成字符串数据我了了个去好了现在生成的签名值已经和官方的签名值的长度一模一样。
Pascal
varsSignByte: TBytes;
begin// 构造用于签名的字符串Result : Format(GET%s%s, [TURI.URLEncode(/), TURI.URLEncode(sCanonicalQueryString)]);// 使用HMAC-SHA1计算返回原始二进制数据sSignByte : THashSHA1.GetHMACAsBytes(Result, sKeyScret );// 得到签名值Result : TURI.URLEncode(TNetEncoding.Base64.EncodeBytesToString(sSignByte));
end; 坑2这下自以为没问题了但是生成的签名值一直没官方的不一样啊编码前的原始字符串貌似也一模一样就是生成的签名值确实另外一个值无奈只能又是一步一步调试并且每一步和官方的值做对比突然发现UTC时间经过URL编码后会把里面的时间分隔符的会编码上%3A在生成签名值前再进行一次URL编码官方的会把签名的“%3A”里面的%再进行一次编码变成了%253A而在Delphi中却没有编码在我的理解中确实不需要再编码了啊因为这里已经按URL编码过一次了这也算是一个坑吧没办法只能按照阿里云的要求对其中的%再进行一次单独的替换。所以就有了下面代码的最后2行代码。
Pascal sBody : Format(AccessKeyId%sActionDescribeSubDomainRecordsFormatjsonSignatureMethodHMAC-SHA1, [sKey.Trim]);sBody : sBody Format(SignatureNonce%sSignatureVersion1.0SubDomain%s, [GetNowStr, LowerCase(sDomain.Trim)]);sBody1 : sBody Format(Timestamp%sVersion2015-01-09, [GetUTC]);sBody : sBody Format(Timestamp%sVersion2015-01-09, [GetUTC.Replace(%, %25)]); 坑3获取UTC时间UTC时间也就是我们说的世界标准时间正常我们获取UTC时间都是带毫秒的这个一点问题都没有而且也都是ISO8601的标准对时间进行格式化但是我发现在生成签名值的时候和官方算出来的虽然一模一样但是每次还是提示签名值不对这个坑整整耗了我一天时间百思不得其解。后来仔细对比官方获取的UTC时间只精确到秒而我获取的UTC时间是精确到毫秒理论上这个没太大的关系但是我尝试和官方同步使用精确到秒之后这个问题竟然给解决了。
Pascal
function GetUTC: string;
// 获取ISO8601格式的UTC世界标准时间
varpTime: _TIME_ZONE_INFORMATION;
beginGetTimeZoneInformation(pTime);// 自带的函数带有毫秒在Update的时候签名值出错所以使用不带毫秒的时间// Result : TURI.URLEncode(DateToISO8601(IncMinute(Now, pTime.bias)));Result : TURI.URLEncode(FormatDateTime(YYYY-MM-DDThh:nn:ssZ, IncMinute(Now, pTime.bias)));
end 经过上面的填坑很快整个代码都修改完成其他的代码都是最最常规的经过测试已经很轻松的能够更新阿里云的域名解析了。加到自己的软件服务端再也不需要安装官方或者其他的AliDDNS软件了。下面将所有的代码贴上拿走不谢如果有看官发现里面的错误或者有新的改动希望能给个反馈哦。
Pascal
// *******************************************************
//
// AliDDNS - 阿里云DDNS自动更新代码
//
// 版权所有 (C) 2019-2020
// 作者: HenryXu
// 更新日期: 2020.03.23
// 联系方式QQ( 55524082 )
// 说明
// 01、本函数只适用于AliYun的动态域名更新解析使用
// 02、域名必须在AliYun上注册
// 03、AliYun的AccessKeyID和AccessKeySecret可控制AliYun的所有权限请妥善保管
// 04、建议开通子用户AccessKey并设置只能修改云解析的权限
// 05、本函数待有优化之处我会及时更新
// 06、有什么建议或修改其中的代码希望能发一份给我以共同修改
//
// *******************************************************unit SQ_AliDDNS;interfaceusesWinapi.Windows, System.Classes, System.SysUtils, System.JSON, System.DateUtils, System.Hash, System.NetEncoding,System.Net.URLClient, System.Net.HttpClient, System.Net.HttpClientComponent, System.Net.Socket;{ -------------------------------------------------------------------------------过程名: UpdateDomainRecord作者: HenryXu日期: 2020.03.21参数: sKey, sKeyScret, sDomain: string参数说明 AccessKeyIDAccessKeySecret域名返回值: string------------------------------------------------------------------------------- }
function UpdateDomainRecord(sKey, sKeyScret, sDomain: string): string;implementationconstApiAliAddr http://alidns.aliyuncs.com/;PubIPAddr http://ip.3322.net/;typeTSubDomainInfo recordRR: string;TTL: string;IP: string;RecordId: string;Output: string;end;function GetUTC: string;
// 获取ISO8601格式的UTC世界标准时间
varpTime: _TIME_ZONE_INFORMATION;
beginGetTimeZoneInformation(pTime);// 自带的函数带有毫秒在Update的时候签名值出错所以使用不带毫秒的时间// Result : TURI.URLEncode(DateToISO8601(IncMinute(Now, pTime.bias)));Result : TURI.URLEncode(FormatDateTime(YYYY-MM-DDThh:nn:ssZ, IncMinute(Now, pTime.bias)));
end;function GetNowStr: string;
beginResult : FormatDateTime(YYYYMMDDhhnnss, Now);
end;function httpSendRequest(sBody: string): string;
// 发送Http请求
varFHttpClient: TNetHttpClient;
beginFHttpClient : TNetHttpClient.Create(nil);tryResult : FHttpClient.Get(sBody).ContentAsString(TEncoding.UTF8);finallyFHttpClient.Free;end;
end;function GetPublicIP: string;
// 获取本机的公网IP
beginResult : httpSendRequest(PubIPAddr).Trim;
end;function GetSignature(sCanonicalQueryString, sKeyScret: string): string;
varsSignByte: TBytes;
begin// 构造用于签名的字符串Result : Format(GET%s%s, [TURI.URLEncode(/), TURI.URLEncode(sCanonicalQueryString)]);// 使用HMAC-SHA1计算返回原始二进制数据sSignByte : THashSHA1.GetHMACAsBytes(Result, sKeyScret );// 得到签名值Result : TURI.URLEncode(TNetEncoding.Base64.EncodeBytesToString(sSignByte));
end;function GetDomainRecordID(sKey, sKeyScret, sDomain: string): TSubDomainInfo;
// 获取AliYun上对应域名的解析信息
varsBody, sBody1: string;oJson: TJSONObject;index: integer;
beginsBody : Format(AccessKeyId%sActionDescribeSubDomainRecordsFormatjsonSignatureMethodHMAC-SHA1, [sKey.Trim]);sBody : sBody Format(SignatureNonce%sSignatureVersion1.0SubDomain%s, [GetNowStr, LowerCase(sDomain.Trim)]);sBody1 : sBody Format(Timestamp%sVersion2015-01-09, [GetUTC]);sBody : sBody Format(Timestamp%sVersion2015-01-09, [GetUTC.Replace(%, %25)]);Result.Output : httpSendRequest(Format(%s?%sSignature%s, [ApiAliAddr, sBody1,GetSignature(sBody, sKeyScret.Trim)]));// 这里从结果中需要取RecordId,RR和TTL三个值oJson : TJSONObject.ParseJSONValue(Trim(Result.Output)) as TJSONObject;if oJson nil thenbegintryif oJson.TryGetValue(TotalCount, index) and (index 0) thenbeginResult.RecordId : oJson.GetValueString(DomainRecords.Record[0].RecordId).Trim;Result.RR : oJson.GetValueString(DomainRecords.Record[0].RR).Trim;Result.IP : oJson.GetValueString(DomainRecords.Record[0].Value).Trim;Result.TTL : oJson.GetValueString(DomainRecords.Record[0].TTL).Trim;end;finallyoJson.Free;end;end;
end;function UpdateDomainRecord(sKey, sKeyScret, sDomain: string): string;
// 更新AliYun上对应域名的IP记录
varsBody, sBody1: string;SubdomainInfo: TSubDomainInfo;
beginif GetPublicIP TIPAddress.LookupName(sDomain).Address thenExit;SubdomainInfo : GetDomainRecordID(sKey, sKeyScret, sDomain);if not SubdomainInfo.RecordId.IsEmpty then// 如果AliYun中存在对应域名的记录begin// 以防万一再次确认公网IP和域名记录里的IP是否一样if GetPublicIP SubdomainInfo.IP thenExit;sBody : Format(AccessKeyId%sActionUpdateDomainRecordFormatjsonRR%s, [sKey.Trim, SubdomainInfo.RR]);sBody : sBody Format(RecordId%sSignatureMethodHMAC-SHA1, [SubdomainInfo.RecordId]);sBody : sBody Format(SignatureNonce%sSignatureVersion1.0TTL%s, [GetNowStr, SubdomainInfo.TTL]);sBody1 : sBody Format(Timestamp%sTypeAValue%sVersion2015-01-09, [GetUTC, GetPublicIP]);sBody : sBody Format(Timestamp%sTypeAValue%sVersion2015-01-09,[GetUTC.Replace(%, %25), GetPublicIP]);Result : httpSendRequest(Format(%s?%sSignature%s, [ApiAliAddr, sBody1, GetSignature(sBody, sKeyScret.Trim)]));endelseResult : SubdomainInfo.Output;
end;end.