一、設(shè)置阿里云域名管理賬號
為了安全,我們新建一個子賬號,僅編程訪問且只有域名修改的權(quán)限。
1.1 登錄阿里云控制臺
1.2 在產(chǎn)品與服務(wù)中搜“訪問控制”

1.3 創(chuàng)建新用戶,只勾選編程訪問。
這里要記住AccessKeyID和AccessKey Secret,后面需要用到。

1.4 添加DNS管理權(quán)限
找到AliyunDNSFullAccess權(quán)限,并添加。

二、編寫程序?qū)崟r判斷本地公網(wǎng)IP并修改阿里云域名解析IP
2.1 新建maven項目,并添加阿里云開發(fā)工具包。
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>aliyun-java-sdk-alidns</artifactId>
<version>2.0.10</version>
</dependency>
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>aliyun-java-sdk-core</artifactId>
<version>4.3.2</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.76</version>
</dependency>
2.2 配置文件
{
"regionId": "cn-hangzhou",
"accessKeyId": "前面創(chuàng)建賬號的AccessKeyID",
"secret": "前面創(chuàng)建賬號的AccessKey Secret",
"tld":"zengwu.com.cn",
"rr": "a",
"dnsInterval": 86400,
"ipInterval": 10
}
- regionId: 區(qū)域,域名管理一般是杭州"cn-hangzhou"
- accessKeyId: 前面創(chuàng)建賬號的AccessKeyID
- secret: 前面創(chuàng)建賬號的AccessKey Secret
- tld: 頂級域名
- rr: 主機名
- dnsInterval: 檢查阿里云DNS配置的間隔時間(秒)
- ipInterval: 檢查本地公網(wǎng)IP的間隔時間(秒)
2.3 程序代碼
package com.zngw.aliddns;
import com.alibaba.fastjson.JSON;
import com.aliyuncs.DefaultAcsClient;
import com.aliyuncs.IAcsClient;
import com.aliyuncs.alidns.model.v20150109.DescribeSubDomainRecordsRequest;
import com.aliyuncs.alidns.model.v20150109.DescribeSubDomainRecordsResponse;
import com.aliyuncs.alidns.model.v20150109.UpdateDomainRecordRequest;
import com.aliyuncs.alidns.model.v20150109.UpdateDomainRecordResponse;
import com.aliyuncs.exceptions.ClientException;
import com.aliyuncs.profile.DefaultProfile;
import com.google.gson.Gson;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* DDNS class
*
* @author 55
* @date 2021/6/28
*/
public class DDNS {
private String recordsIp = ""; // 域名IP
private long lastDnsTime = 0; // 上次域名查詢時間
private long lastIpTime = 0; // 上次公網(wǎng)ip查詢時間
private String host = null; // 完整域名
private Config cfg = null; // 配置文件
/**
* 初始化
*/
private void init(String filename){
String cfgStr = readJsonFile(filename);
cfg = JSON.parseObject(cfgStr, Config.class);
if (cfg == null){
System.out.println("讀取配置文件"+filename+"失敗");
return;
}
cfg.dnsInterval *= 1000;
cfg.ipInterval *= 1000;
host = cfg.rr + "." +cfg.tld;
if (cfg.rr == null || cfg.rr.length() == 0 || "@".equals(cfg.rr)){
// 頂級域名處理
cfg.rr = "@";
host = cfg.tld;
}
}
/**
* 獲取當前主機公網(wǎng)IP
*/
private String getCurrenHostIp() {
// 這里使用jsonip.com第三方接口獲取本地IP
String jsonip = "https://jsonip.com";
// 接口返回結(jié)果
String result = "";
BufferedReader in = null;
try {
// 使用HttpURLConnection網(wǎng)絡(luò)請求第三方接口
URL url = new URL(jsonip);
HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
urlConnection.setRequestMethod("GET");
urlConnection.connect();
in = new BufferedReader(new InputStreamReader(urlConnection.getInputStream()));
String line;
while((line = in.readLine()) != null) {
result += line;
}
} catch (Exception e) {
e.printStackTrace();
}
// 使用finally塊來關(guān)閉輸入流
finally {
try {
if (in != null) {
in.close();
}
} catch (Exception e2) {
e2.printStackTrace();
}
}
// 正則表達式,提取xxx.xxx.xxx.xxx,將IP地址從接口返回結(jié)果中提取出來
String rexp = "(\\d{1,3}\\.){3}\\d{1,3}";
Pattern pat = Pattern.compile(rexp);
Matcher mat = pat.matcher(result);
String res = "";
while (mat.find()) {
res = mat.group();
break;
}
return res;
}
/**
* 獲取主域名的所有解析記錄列表
*/
private DescribeSubDomainRecordsResponse describeSubDomainRecords(DescribeSubDomainRecordsRequest request, IAcsClient client) {
try{
// 調(diào)用SDK發(fā)送請求
return client.getAcsResponse(request);
} catch (ClientException e) {
e.printStackTrace();
// 發(fā)生調(diào)用錯誤,拋出運行時異常
throw new RuntimeException();
}
}
/**
* 修改解析記錄
*/
private UpdateDomainRecordResponse updateDomainRecord(UpdateDomainRecordRequest request, IAcsClient client) {
try {
// 調(diào)用SDK發(fā)送請求
return client.getAcsResponse(request);
} catch (ClientException e) {
e.printStackTrace();
// 發(fā)生調(diào)用錯誤,拋出運行時異常
throw new RuntimeException();
}
}
/**
* 檢測IP是否改變,改變了就修改
*/
private void check(String ip){
// 設(shè)置鑒權(quán)參數(shù),初始化客戶端
DefaultProfile profile;
profile = DefaultProfile.getProfile(cfg.regionId,cfg.accessKeyId,cfg.secret);
IAcsClient client = new DefaultAcsClient(profile);
//查詢指定域名的最新解析記錄
DescribeSubDomainRecordsRequest describeSubDomainRecordsRequest = new DescribeSubDomainRecordsRequest();
describeSubDomainRecordsRequest.setSubDomain(host);
DescribeSubDomainRecordsResponse describeSubDomainRecordsResponse = describeSubDomainRecords(describeSubDomainRecordsRequest, client);
List<DescribeSubDomainRecordsResponse.Record> domainRecords = describeSubDomainRecordsResponse.getDomainRecords();
//最新的一條解析記錄
if (domainRecords.size() != 0) {
DescribeSubDomainRecordsResponse.Record record = domainRecords.get(0);
// 記錄ID
String recordId = record.getRecordId();
// 記錄值
recordsIp = record.getValue();
if (!ip.equals(recordsIp)) {
// 修改解析記錄
UpdateDomainRecordRequest updateDomainRecordRequest = new UpdateDomainRecordRequest();
// 主機記錄
updateDomainRecordRequest.setRR(cfg.rr);
// 記錄ID
updateDomainRecordRequest.setRecordId(recordId);
// 將主機記錄值改為當前主機IP
updateDomainRecordRequest.setValue(ip);
// 解析記錄類型
updateDomainRecordRequest.setType("A");
UpdateDomainRecordResponse updateDomainRecordResponse = updateDomainRecord(updateDomainRecordRequest, client);
System.out.println("域名 "+host+" 解析地址已修改為:" + ip);
recordsIp = ip;
}
}
}
/**
* 運行檢測
*/
private void run(){
while (true){
long now = System.currentTimeMillis();
// 當前主機公網(wǎng)IP
String ip = getCurrenHostIp();
// 查詢域名服務(wù)器值是否改變
if (lastDnsTime < now){
check(ip);
lastIpTime = now + cfg.ipInterval;
lastDnsTime = now + cfg.dnsInterval;
}
// 公網(wǎng)IP是否發(fā)生改變
if (lastIpTime < now){
if (ip!=null && !ip.equals(recordsIp)){
check(ip);
lastDnsTime = now + cfg.dnsInterval;
}
lastIpTime = now + cfg.ipInterval;
}
try {
// 進程睡眠釋放CPU并達到延遲效果
Thread.sleep(Math.min( lastIpTime,lastDnsTime) - now -1);
}catch(InterruptedException e)
{
e.printStackTrace();
}
}
}
/**
* 讀取json文件,返回json串
* @param fileName
* @return
*/
public String readJsonFile(String fileName) {
String jsonStr = "";
try {
File jsonFile = new File(fileName);
FileReader fileReader = new FileReader(jsonFile);
Reader reader = new InputStreamReader(new FileInputStream(jsonFile), "utf-8");
int ch = 0;
StringBuffer sb = new StringBuffer();
while ((ch = reader.read()) != -1) {
sb.append((char) ch);
}
fileReader.close();
reader.close();
jsonStr = sb.toString();
return jsonStr;
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
public static void main(String[] args) {
String filename = "./config.json";
DDNS ddns = new DDNS();
ddns.init(filename);
ddns.run();
}
}
三、運行
編譯好jar程序,將jar和config.json放到同一目錄下,直接后臺運行jar就可以了。
附:完整mavn項目