Clash HTTP API接口详解及自动换IP办法

1.接口地址配置

我们打开clash的配置主文件夹,找到config.yaml,查看关于api端口和密码的设置。

以下是一个config.yaml文件示例:

mixed-port: 7890
allow-lan: true
external-controller: 127.0.0.1:53365
secret: 29c0c9ba-1740-42c0-bb8c-dd316c5d1e4a
ipv6: false
log-level: info

其中external-controller为API的IP和端口配置,如果需要其他机器能够访问这个接口,需要将IP设置为0.0.0.0,secret为API接口密码。

以下是ClashForWindows打开config.yaml文件夹的方法

Clash HTTP API接口详解及自动换IP办法
windows打开config.yaml文件主目录的方法

2.ClashForWindows常用HTTP接口详细介绍

获取所有代理

GET /proxies

获取 Clash 中所有的代理200 代理类型: Direct | Reject | Selector | Shadowsocks | Socks5 | URLTest 类型为 Selector 时有 all 和 now 两个字段,分别代表所含有的代理以及现在选中的代理 类型未 URLTest 时 now 字段为现在最快的代理名字

{
    "proxies": {
        "DIRECT": {
            "type": "Direct"
        },
        "GLOBAL": {
            "all": [
                "Proxy",
                "auto",
                "🇭🇰"
            ],
            "now": "Proxy",
            "type": "Selector"
        },
        "Proxy": {
            "all": [
                "auto",
                "🇭🇰"
            ],
            "now": "auto",
            "type": "Selector"
        },
        "REJECT": {
            "type": "Reject"
        },
        "auto": {
            "now": "🇭🇰",
            "type": "URLTest"
        },
        "🇭🇰": {
            "type": "Shadowsocks"
        }
    }
}

获取单个代理的延迟

GET /proxies/:name/delay

获取 Clash 中单个代理的延迟

Path Parameters

Name Type Description
name string 代理名 (大小写敏感)

Query Parameters

Name Type Description
timeout number 超时时间 (ms)
url string 测试代理的目标网址

返回状态码:200 正常返回、400请求格式错误、404代理不存在、408代理请求超时

{
    "delay": 200
}

切换 Selector 中选中的代理

PUT /proxies/:name

当前接口只支持切换 Selector 中的代理

Path Parameters

Name Type Description
name string 代理名称 (大小写敏感)

Request Body

Name Type Description
name string 要切换的代理名字

返回状态码:204 切换成功、400请求错误、404代理不存在

获得当前的基础设置

GET /configs

{
    "port": 7890,
    "socket-port": 7891,
    "redir-port": 0,
    "allow-lan": true,
    "mode": "Rule",
    "log-level": "info"
}

增量修改配置

PATCH /configs

传入需要修改的配置即可

Request Body

Name Type Description
port integer HTTP 代理端口
socks-port integer Socks5 代理端口
redir-port string Redir 代理端口
allow-lan boolean 是否把代理监听地址放到 0.0.0.0
mode string 代理模式 ( Global | Rule | Direct )
log-level string 设置 TTY 日志等级 ( info | warning | error | debug )

返回状态码:204 由于可能出现 RESTful API 流量经过代理的问题,而更换端口需要 Graceful Shutdown 所以统一返回 204 No Content

3.自动换IP方法

参考上述常用API接口,我们可通过”GET/proxies获取所有代理”和”PUT /proxies/:name切换 Selector 中选中的代理”两个接口结合,完成自动获取代理节点,随机更换当前代理节点。(为获取活跃且低延迟的节点,可能需要增加一个定时任务,通过”GET /proxies/:name/delay延迟获取”方法更新节点延迟信息。)

以下是一个java版实现Clash切换IP的功能,此处使用了hutool工具类库。需要提前引入相关的maven依赖。


import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.net.URLEncodeUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import cn.hutool.http.HttpUtil;
import cn.hutool.http.Method;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class ClashUtil {

    private static final Logger log = LoggerFactory.getLogger(ClashUtil.class);


    /**
     * clash配置
     * external-controller: 127.0.0.1:61920
     * secret: 4a1c185f-607e-4ec6-a984-bab952503d5d
     */


    public static  String clashApiUrl = "http://172.17.0.1:61920";


    public static  String socks5ProxyHost = "172.17.0.1";

    public static  int socks5ProxyPort = 7891;


    public static  String secret = "4a1c185f-607e-4ec6-a984-bab952503d5d";

    //public static  String secret = "";


    public static  String selectorName = "GLOBAL";

    public static String curNode = null;

    public static final List<String> excludeNodes = Arrays.asList(new String[]{"自动选择","故障转移","实验","免费","国内","漏网","Cloudflare"
            ,"Steam","选择","爱奇","动画","DIRECT","REJECT","OneDrive"});

    public static List<String> getAllNode(){
        HttpRequest request = HttpUtil.createGet(clashApiUrl+"/proxies");
        if(StrUtil.isNotEmpty(secret)){
            request.header("Authorization","Bearer "+secret);
        }
        String result = request.execute().body();
        if(StrUtil.isEmpty(result) || !JSONUtil.isTypeJSONObject(result)){
            return null;
        }
        JSONObject resultObj = JSONUtil.parseObj(result).getJSONObject("proxies");
        JSONArray jsonArray = resultObj.getJSONObject(selectorName).getJSONArray("all");
        List<String> validNodeList = new ArrayList<>();
        for (int i = 0; i < jsonArray.size(); i++) {
            String itemNodeName = jsonArray.getStr(i);
            boolean isExcludeNodes = false;
            for (String excludeNode : excludeNodes) {
                if(itemNodeName.contains(excludeNode)){
                    isExcludeNodes = true;
                    break;
                }
            }
            if(isExcludeNodes){
                continue;
            }
            boolean use = true;

            //延迟计算
            JSONObject nodeDelay = resultObj.getJSONObject(itemNodeName);
            //失效节点
            if(nodeDelay==null  || nodeDelay.getJSONArray("history")==null || nodeDelay.getJSONArray("history").size()==0){
                continue;
            }
            int maxDelay = 1000;

            JSONArray delayArray = nodeDelay.getJSONArray("history");
            for (int i1 = 0; i1 < delayArray.size(); i1++) {
                JSONObject itemDelay = delayArray.getJSONObject(i1);
                Integer itemDelayInt = itemDelay.getInt("delay");
                if(itemDelayInt>maxDelay || itemDelayInt==0){
                    use = false;
                    break;
                }
            }
            if(use){
                //System.out.println(itemNodeName);
                validNodeList.add(itemNodeName);
            }
        }
        return validNodeList;
    }


    public static void testAllNode(){
        HttpRequest request = HttpUtil.createGet(clashApiUrl+"/proxies");
        if(StrUtil.isNotEmpty(secret)){
            request.header("Authorization","Bearer "+secret);
        }
        String result = request.execute().body();
        if(StrUtil.isEmpty(result) || !JSONUtil.isTypeJSONObject(result)){
            return ;
        }
        JSONObject resultObj = JSONUtil.parseObj(result).getJSONObject("proxies");
        JSONArray jsonArray = resultObj.getJSONObject(selectorName).getJSONArray("all");
        for (int i = 0; i < jsonArray.size(); i++) {
            String itemNodeName = jsonArray.getStr(i);
            boolean isExcludeNodes = false;
            for (String excludeNode : excludeNodes) {
                if(itemNodeName.contains(excludeNode)){
                    isExcludeNodes = true;
                    break;
                }
            }
            if(isExcludeNodes){
                continue;
            }
            //测速
            try {
                HttpUtil.get(clashApiUrl+"/proxies/"+ URLEncodeUtil.encodeAll(itemNodeName) +"/delay?timeout=5000&url=http%3A%2F%2Fwww.gstatic.com%2Fgenerate_204", 10000);
            } catch (Exception e) {

            }
        }
    }


    public static boolean changeRandomNode(){
        List<String> allNode = getAllNode();
        if(CollectionUtil.isEmpty(allNode) || allNode.size()<10){
            log.error("not enough node for change");
            return false;
        }
        String selectNode = RandomUtil.randomEle(allNode);
        HttpRequest request = HttpUtil.createRequest(Method.PUT,clashApiUrl+"/proxies/"+ selectorName);
        request.header("Authorization","Bearer "+secret);
        JSONObject requestBodyObj = new JSONObject();
        requestBodyObj.putOnce("name",selectNode);
        request.body(requestBodyObj.toString());
        HttpResponse response = request.execute();
        if(response.getStatus()==204){
            curNode = selectNode;
            log.info("change node success:{}",selectNode);
            return true;
        }
        log.error("change node error,:{}",response.body());
        return false;
    }

    public static void main(String[] args) {
        changeRandomNode();
    }

}

自动换IP功能可广泛用于自动化注册、网络爬虫等场景。

正文完