应用场景

本人所在单位启用了内部OA(办公自动化)系统,由于有许多部门并不在公司总部,不处于同一个局域网内,外围单位无法直接访问内网的OA服务器,影响了OA系统在总公司的推行。为了解决这一问题曾试图通过VPN来从互联网接入,但由于外围单位分别通过不同的ISP(Internet Service Provider)接入互联网的,导致VPN服务器配置的不一致,不能全面畅通接入,最终选择了使用代理服务器的接入方案。外围单位可以从互联网通过代理服务器访问内网的OA服务器。

外网用户在访问公司OA服务器时,就需要在IE浏览器中配置代理服务器,配置方法见图1。
image.jpeg
图1 设置代理服务器

配置好IE后就可以访问内网OA服务器了。但这样一来,就存在一个问题,就是外网用户在访问OA时需要在IE中设置代理服务器,而在访问互联网时又需要取消这一配置,不使用代理服务器而直接访问。每次都需要手动配置,十分不便,对于“菜鸟”而言就更显得繁琐了。

如何让IE浏览器自动实现配置的转换呢?这就需要用到代理自动配置脚本(PAC-file:Proxy Auto-Config file)了。自动配置脚本也就是PAC脚本,这是一种以.PAC为扩展名的JavaScript脚本,PAC脚本其实就是定义一个名为“FindProxyForURL”的Java Script函数,该函数会被浏览器自动调用,从而实现代理服务器的自动转换。

由于我们的代理是反向(由外向内)的,脚本的具体内容如下:

  1. function findproxyforurl(host,url)
  2. {
  3. if (shexpmatch(host,"*.jigang.com.cn"))
  4. return "proxy 218.XXX.XXX.30:8080"
  5. else if (shexpmatch(host,"172.16.*.*"))
  6. return "proxy 218.XXX.XXX.30:8080"
  7. else
  8. return "direct"
  9. }

这段脚本的含义就是:如果是访问内网OA的请求(域名为jigang.com.cn),则使用代理,如果访问内网的某些网址(IP地址为:172.16..),则使用代理,除此之外的所有请求则不使用代理而直接连接。

将上述脚本内容保存为一个扩展名为PAC的脚本文件,如C:\proxy.pac。

然后我们设置IE浏览器把“自动配置脚本选项”指向它,完成集中设置代理配置的工作。我们只需一次性配置完毕,让IE自动识别是否使用代理服务器,而无需手动转换,从而实现访问内、外网站的自动转换。

IE的代理设置里面有一个“使用自动配置脚本”的选项,这里的具体设置如下:

自动配置脚本设置:打开浏览器,选择“工具/Internet选项/连接/局域网配置”,随后勾选“使用自动脚本配置”项,最后输入自动配置脚本所存在地址即可(比如file ://C:\proxy.pac,如图2)。
image.jpeg
图2 设置本机自动配置脚本
当然我们也可以将这个脚本文件放在Web服务器上,这样不用为每台客户机都写一个PAC文件了,只需要在“使用自动配置脚本”的地址处填入相关的IP地址就行了。比如:http ://218.XXX.XXX.30/proxy.pac。在IE中的设置见图3。
image.jpeg
图3 设置网络自动配置脚本

这样做的好处是,我们可以随意修改代理脚本从而改变代理服务器的地址或端口等而不用去修改每台客户机的PAC文件了。

什么是 PAC

PAC,一个自动代理配置脚本,包含了很多使用 **JavaScript **编写的规则,它能够决定网络流量走默认通道还是代理服务器通道,控制的流量类型包括:HTTP、HTTPS 和 FTP。
它是一段 JavaScript 脚本:

  1. function FindProxyForURL(url, host) {
  2. return "DIRECT";
  3. }

上面就是一个最简洁的 PAC 文件,意思是所有流量都直接进入互联网,不走代理。

PAC脚本主体是一个 FindProxyForURL 的JS函数。函数的反回值有三类

一个PAC文件其实就是一个文本文件,最简单的格式就是包含一个叫FindProxyForURL的JScript函数,IE通过传入两个变量来调用这个函数,一个是用户浏览的地址URL全路经,一个是这个URL中的主机名部分(host)。

PAC 语法和函数

上面函数中,url 字段就是我们在浏览器地址栏输入的待访问地址,host 为该地址对应的 hostname,return 语句有三种指令:

  • DIRECT,表示无代理直接连接
  • PROXY host:port,表示走host:port 的 proxy 服务
  • SOCKS host:port,表示走host:port 的 socks 服务

而返回的接口可以是多个代理串联:

  1. return "PROXY 222.20.74.89:8800; SOCKS 222.20.74.89:8899; DIRECT";

上面代理的意思是,默认走222.20.74.89:8800 的 proxy 服务;如果代理挂了或者超时,则走 222.20.74.89:8899的 socks 代理;如果 socks 也挂了,则无代理直接连接。从这里可以看出 PAC 的一大优势:自动容灾

PAC 提供了几个内置的函数,下面一一介绍下:

dnsDomainIs

类似于 ==,但是对大小写不敏感,

  1. if (dnsDomainIs(host, "google.com") ||
  2. dnsDomainIs(host, "www.google.com")) {
  3. return "DIRECT";
  4. }

shExpMatch

Shell 正则匹配, 匹配用的比较多,可以是.http://example.com,也是可以下面这样,

  1. if (shExpMatch(host, "vpn.domain.com") ||
  2. shExpMatch(url, "http://abcdomain.com/folder/*")) {
  3. return "DIRECT";
  4. }

isInNet

判断是否在网段内容,比如 10.1.0.0 这个网段,10.1.1.0 就在网段中,

  1. if (isInNet(dnsResolve(host), "172.16.0.0", "255.240.0.0")) {
  2. return "DIRECT";
  3. }

myIpAddress

返回主机的 IP,

  1. if (isInNet(myIpAddress(), "10.10.1.0", "255.255.255.0")) {
  2. return "PROXY 10.10.5.1:8080";
  3. }

dnsResolve

通过 DNS 查询主机 ip,

  1. if (isInNet(dnsResolve(host), "10.0.0.0", "255.0.0.0") ||
  2. isInNet(dnsResolve(host), "172.16.0.0", "255.240.0.0") ||
  3. isInNet(dnsResolve(host), "192.168.0.0", "255.255.0.0") ||
  4. isInNet(dnsResolve(host), "127.0.0.0", "255.255.255.0")) {
  5. return "DIRECT";
  6. }

isPlainHostName

判断是否为诸如http://barret/http://server-name/ 这样的主机名,

  1. if (isPlainHostName(host)) {
  2. return "DIRECT";
  3. }

isResolvable

判断主机是否可访问,

  1. if (isResolvable(host)) {
  2. return "PROXY proxy1.example.com:8080";
  3. }

dnsDomainLevels

返回是几级域名,比如dnsDomainLevels(http://barretlee.com) 返回的结果就是 1,

  1. if (dnsDomainLevels(host) > 0) {
  2. return "PROXY proxy1.example.com:8080";
  3. } else {
  4. return "DIRECT";
  5. }

weekdayRange

周一到周五,

  1. if (weekdayRange("MON", "FRI")) {
  2. return "PROXY proxy1.example.com:8080";
  3. } else {
  4. return "DIRECT";
  5. }

dateRange

一月到五月,

  1. if (dateRange("JAN", "MAR")) {
  2. return "PROXY proxy1.example.com:8080";
  3. } else {
  4. return "DIRECT";
  5. }

timeRange

八点到十八点,

  1. if (timeRange(8, 18)) {
  2. return "PROXY proxy1.example.com:8080";
  3. } else {
  4. return "DIRECT";
  5. }

alert

据说这个函数可以用来调试,不过我在 Chrome 上测试并未生效,

  1. resolved_host = dnsResolve(host);
  2. alert(resolved_host);

PAC 文件的安装和注意事项

在 Windows 系统中,通过「Internet选项 -> 连接 -> 局域网设置 -> 使用自动配置脚本」可以找到配置处,下放的地址栏填写 PAC 文件的 URI,这个 URI 可以是本地资源路径(file:///),也可以是网络资源路径(http://)。
Chrome 中可以在「chrome://settings/ -> 显示高级设置 -> 更改代理服务器设置」中找到 PAC 填写地址。

需要注意的几点:**

  • PAC 文件被访问时,返回的文件类型(Content-Type)应该为:application/x-ns-proxy-autoconfig,当然,如果你不写,一般浏览器也能够自动辨别
  • FindProxyByUrl(url, host) 中的 host 在上述函数对比时无需转换成小写,对大小写不敏感
  • 没必要对 dnsResolve(host) 的结果做缓存,DNS 在解析的时候会将结果缓存到系统中