customer-service-btn联系客服
首页to其他to文章详情

代理认证(用户名密码)在不同编程语言中的正确配置方法

代理认证(用户名密码)在不同编程语言中的正确配置方法Daniel Wong
dateTime2026-11-02 09:35
dateTime其他

用代理发起请求时,服务商通常会提供一组账号密码。

地址填进代码,请求却返回 407 Proxy Authentication Required;或者密码里的 @、: 让连接直接失败。这两类情况在配置代理时最常遇到。

本文将按 HTTP 和 SOCKS5 两种协议讲清认证发生在哪一层,并给出 Python、Java、cURL、Node.js、PHP、C# 六种语言的最小配置和 407 的排查顺序。

代理认证(用户名密码)在不同编程语言中的正确配置方法

代理用户名密码认证的基本原理

代理认证发生在客户端与代理服务器之间,和登录目标网站是两件事。代理只校验“这次连接有没有被授权”,不校验你在目标站点上的账号。

1. HTTP Basic Authentication

客户端第一次请求若未带凭据,代理返回 407,并用 Proxy-Authenticate 头说明自己支持哪种认证方案:

HTTP/1.1 407 Proxy Authentication Required
Proxy-Authenticate: Basic realm="proxy"

客户端重发时把凭据放进 Proxy-Authorization 头,格式是方案名、一个空格,再加 用户名:密码 的 Base64 编码:

Proxy-Authorization: Basic dXNlcjpwYXNzd29yZA==

这套机制由 RFC 7617 定义,407 与 Proxy-Authenticate 由 RFC 9110 定义。目标站点返回的 401 属于另一层,它要求的是站点身份认证,和代理账号无关。

2. SOCKS5 用户名密码认证(RFC 1929)

SOCKS5 不解析 HTTP 报文,凭据也不经过请求头。它在建立 TCP 通道前先协商认证方法,双方选定用户名密码方式后,进入 RFC 1929 定义的子协商完成校验。协议本身由 RFC 1928 定义。

它和 HTTP Basic 都是账号密码,但一个走握手阶段,一个走 HTTP 头,排查方式也不同。

3. 代理 URL 格式与特殊字符编码

最常见的写法是把凭据嵌进代理地址:

http://username:password@proxy_host:proxy_port

问题在于 @、:、/、#、? 本身就是 URL 的结构符号。密码是 p@ss 时,解析器会把第一个 @ 当成“凭据结束、主机开始”的分隔符,主机名于是变成 ss@proxy_host。

处理办法是对用户名和密码分别做百分号编码,而不是把整条地址一起编码。

字符编码结果出现在凭据里会怎样
@%40被当作凭据与主机的分隔符
:%3A被当作用户名与密码的分隔符
/%2F被当作路径的起点
#%23后面的内容被当成片段丢弃
?%3F后面的内容被当成查询参数

Python 里用 quote 处理,safe 参数传空字符串表示连 / 也一并编码:

from urllib.parse import quote

username = quote("user@name", safe="")
password = quote("pass:word", safe="")

不同库处理凭据的时机不一样。libcurl 的 CURLOPT_PROXYUSERPWD 会先把传入值做 URL 解码,所以给 curl 传 user%40name 会被还原成 user@name;Java、.NET 的认证对象直接接收原始字符串,编码过的值反而成了密码的一部分。判断标准是:拼进 URL 的凭据要编码,单独传入的凭据对象不编码。

还有一点常被忽略:Base64 不是加密。它只是把字节换成可打印字符,谁都能还原。HTTP Basic 和 RFC 1929 都不对凭据本身加密,能保护凭据的是承载它的连接,不是这层编码。


不同编程语言中的代理认证配置

下面的示例都请求 https://httpbin.org/ip,它回显请求来源的 IP,用来确认出口是否真的换成了代理。

示例里的地址、端口、账号是占位值,替换成服务商给的实际参数即可;IPDEEP 的静态住宅和动态住宅代理也使用同样的用户名密码格式。

1. Python:Requests(HTTP/HTTPS 与 SOCKS5)

proxies 字典接受带凭据的代理 URL。http 和 https 两个键指目标地址的协议,不是代理本身的协议——访问 HTTPS 目标也要配在 https 键下:

import requests

proxy = "http://your_username:your_password@gateway.example.com:8080"
proxies = {"http": proxy, "https": proxy}

resp = requests.get("https://httpbin.org/ip", proxies=proxies, timeout=10)
print(resp.json())

账号或密码含特殊字符时,先按上一节的做法编码,再拼进 URL。

SOCKS5 需要先 pip install requests[socks]。socks5h:// 由代理端解析目标域名,本地 DNS 不可信时更合适:

proxies = {
    "http": "socks5h://user:password@gateway.example.com:1080",
    "https": "socks5h://user:password@gateway.example.com:1080",
}

2. Java:Apache HttpClient 4.x

CredentialsProvider 负责提供凭据,HttpHost 指定代理,两者合起来就把账号密码绑定到了这个代理上。

下面这段用的是 org.apache.http.*,属于 4.x;5.x 的包名是 org.apache.hc.client5.*,类名和方法都变了,不能直接复制:

import org.apache.http.HttpHost;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;

public class ProxyDemo {
    public static void main(String[] args) throws Exception {
        CredentialsProvider creds = new BasicCredentialsProvider();
        creds.setCredentials(new AuthScope("gateway.example.com", 8080),
                new UsernamePasswordCredentials("your_username", "your_password"));

        HttpHost proxy = new HttpHost("gateway.example.com", 8080);
        try (CloseableHttpClient client = HttpClients.custom()
                .setDefaultCredentialsProvider(creds)
                .setProxy(proxy)
                .build()) {
            System.out.println(EntityUtils.toString(
                    client.execute(new HttpGet("https://httpbin.org/ip")).getEntity()));
        }
    }
}

AuthScope 限定了凭据的适用范围,只有请求同一主机同一端口时才带上。

3. cURL:命令行参数与环境变量

三种常用方式,各一条命令。HTTP 代理:

curl -x http://your_username:your_password@gateway.example.com:8080 https://httpbin.org/ip

环境变量要注意大小写。出于 CGI 场景的安全考虑,curl 会忽略全大写的 HTTP_PROXY(它可能被外部注入),只认小写的 http_proxy;HTTPS_PROXY 大写则有效:

export http_proxy="http://your_username:your_password@gateway.example.com:8080"
export HTTPS_PROXY="http://your_username:your_password@gateway.example.com:8080"

curl https://httpbin.org/ip

SOCKS5 用 socks5h://,域名解析交给代理端:

curl -x socks5h://your_username:your_password@gateway.example.com:1080 https://httpbin.org/ip

密码写在命令行会留在 shell 历史里,长期运行的脚本更适合放进权限受控的配置文件或凭据管理工具。

4. Node.js:Axios + https-proxy-agent

Axios 自带代理配置,与自定义 Agent 同时生效会互相干扰,所以显式关掉 proxy。示例请求 HTTPS 地址,只需给 httpsAgent:

const axios = require('axios');
const { HttpsProxyAgent } = require('https-proxy-agent');

const proxyUrl = 'http://your_username:your_password@gateway.example.com:8080';
const agent = new HttpsProxyAgent(proxyUrl);

axios.get('https://httpbin.org/ip', { httpsAgent: agent, proxy: false })
  .then(response => console.log('出口 IP 信息:', response.data))
  .catch(error => console.error('代理连接错误:', error.message));

要同时代理 HTTP 目标,得再建一个 Agent 分别赋给 httpAgent 和 httpsAgent。另外 https-proxy-agent 只处理 HTTP/HTTPS 代理,SOCKS5 要用 socks-proxy-agent。

5. PHP:cURL 扩展

CURLOPT_PROXYUSERPWD 传“用户名:密码”,CURLOPT_PROXYAUTH 指定认证方式:

<?php
$ch = curl_init('https://httpbin.org/ip');

curl_setopt_array($ch, [
    CURLOPT_PROXY          => 'gateway.example.com',
    CURLOPT_PROXYPORT      => 8080,
    CURLOPT_PROXYTYPE      => CURLPROXY_HTTP,
    CURLOPT_PROXYAUTH      => CURLAUTH_BASIC,
    CURLOPT_PROXYUSERPWD   => 'your_username:your_password',
    CURLOPT_RETURNTRANSFER => true,
]);

echo curl_exec($ch);

用户名含冒号时,CURLOPT_PROXYUSERPWD 会在第一个冒号处切错,改用 CURLOPT_PROXYUSERNAME 和 CURLOPT_PROXYPASSWORD 分开设置;密码含 @、% 时先按 libcurl 的要求做 URL 编码。

6. C#:HttpClient + WebProxy

WebProxy 配 NetworkCredential 是 .NET 里的标准写法,下面用 .NET 6 起的顶层语句:

using System;
using System.Net;
using System.Net.Http;

var proxy = new WebProxy("http://gateway.example.com:8080")
{
    Credentials = new NetworkCredential("your_username", "your_password")
};

var handler = new HttpClientHandler { Proxy = proxy, UseProxy = true };
var client = new HttpClient(handler);

Console.WriteLine(await client.GetStringAsync("https://httpbin.org/ip"));

凭据对象接收原始字符串,不需要百分号编码。生产环境里记得给 HttpClient 设超时,并复用同一个实例。


代理认证常见错误及解决方法

现象常见原因处理方向
407凭据错误、未带认证头、代理要求其他方案看 Proxy-Authenticate 里的方案,再核对账号与传输方式
连接失败、主机名异常密码中的 @、:、/ 未编码用户名和密码分别百分号编码
浏览器配 SOCKS5 报认证失败Chromium 原生不支持 SOCKS5 用户名密码认证改用 HTTP/HTTPS 代理或支持该认证的客户端
Windows 正常、Linux 失败企业代理用 NTLM、Kerberos 等集成认证不要假设所有代理都支持 Basic
凭据出现在仓库或日志里硬编码、命令行传参、日志未脱敏交给环境变量或密钥管理服务

1. 407 不一定是账号密码错了

407 只说明这次连接的认证没通过。最省时间的判断是看响应头:Proxy-Authenticate 写的是 Basic 还是 NTLM,直接决定后面查凭据还是查认证方式。

把同一组账号密码丢进 curl -v -x 跑一遍,能看到请求有没有带上 Proxy-Authorization,比在代码里猜快。

2. SOCKS5 认证与浏览器限制

Chromium 内核的浏览器不实现 SOCKS5 的用户名密码认证方法,配了带认证的 SOCKS5 代理,握手阶段就会失败,界面里也没有填账号密码的地方。

可行做法是改用支持 Basic 的 HTTP/HTTPS 代理,或换成支持 SOCKS5 认证的客户端与自动化工具。

3. 凭据保存与传输

代理账号密码属于基础设施凭据,稳妥的做法是运行时从环境变量、密钥管理服务或权限受控的配置文件读取,不写进源码,也不提交仓库。

日志同样要处理:把代理 URL 直接打进日志,等于把密码写进了日志文件。


总结

代理认证的坑大多不在语言,而在协议层次:HTTP 代理认 Proxy-Authorization 头,SOCKS5 认握手里的子协商;拼进 URL 的凭据要分别编码,单独传入的凭据对象不要编码。

407 出现时先看 Proxy-Authenticate 要求的方案,再核对凭据和地址,比反复改密码有效。

IPDEEP 提供静态住宅代理和动态住宅代理,支持用户名密码认证与 HTTP、HTTPS、SOCKS5 协议,具体地区、认证方式和套餐参数以产品页面为准。

ad2
文章大纲
关注我们
百家号搜狐号B站YouTubeXTelegramGitHub知乎