公众号官网说明:点击进去官方文档
如果公众号基于消息接收安全上的考虑,需要获知微信服务器的IP地址列表,以便识别出哪些消息是微信官方推送给你的,哪些消息可能是他人伪造的,可以通过该接口获得微信服务器IP地址列表。
接口调用请求说明
http请求方式: GET https://api.weixin.qq.com/cgi-bin/getcallbackip?access_token=ACCESS_TOKEN
参数说明
参数 | 是否必须 | 说明 |
---|---|---|
access_token | 是 | 公众号的access_token |
返回说明
正常情况下,微信会返回下述JSON数据包给公众号:
{ "ip_list":["127.0.0.1","127.0.0.1"] }
参数 | 说明 |
---|---|
ip_list | 微信服务器IP地址列表 |
错误时微信会返回错误码等信息,JSON数据包示例如下(该示例为AppID无效错误):
{"errcode":40013,"errmsg":"invalid appid"}
企业号官网说明:点击进去官方文档
获取微信服务器的ip段
- 请求说明
Https请求方式: GET
https://qyapi.weixin.qq.com/cgi-bin/getcallbackip?access_token=ACCESS_TOKEN
- 参数说明
参数 | 必须 | 说明 |
---|---|---|
access_token | 是 | 调用接口凭证 |
- 返回结果
{ "ip_list": ["101.226.103.*", "101.226.62.*"] }
把自己公众号的accesstoken 替换请求地址的ACCESS_TOKEN,然后get请求就可以换取到服务器的ip地址列表了,
但是有一些需要注意的地方(请看返回数据下面的内容);
公证号的ipv4地址段
{
"ip_list": [
"101.226.62.77",
"101.226.62.78",
"101.226.62.79",
···
"180.163.15.170",
"101.226.103.0\/25",
"101.226.233.128\/25",
"58.247.206.128\/25",
"182.254.86.128\/25",
"103.7.30.21",
"103.7.30.64\/26",
"58.251.80.32\/27",
"183.3.234.32\/27",
"121.51.130.64\/27"]
}
企业号的ipv4地址段
{
"ip_list": [
"101.226.103.*",
"101.226.125.*",
"101.226.62.*",
"103.7.30.*",
"112.5.138.*",
"112.90.78.*",
"117.135.171.*",
"120.198.199.*",
"140.207.54.*",
"183.61.32.*",
"203.205.167.*"]
}
请注意:
在返回的数据的地址数据中,公众号中有"101.226.103.0\/25",其中的\/是转义字符/,该地址就是"101.226.103.0/25" ,也就是说这是一个带有子网掩码的ip地址;
打印来自微信的请求头
host:你的服务器网址
user-agent:Mozilla/4.0
content-length:534
accept:*/*
cache-control:no-cache
content-type:text/xml
pragma:no-cache
x-real-ip:58.247.206.153
所以我们不能通过request.getRemoteAddr()获取微信请求的地址,
所以通过request.getHeader("x-real-ip");
同时你也应该发现ip地址58.247.206.153并没有在上面获取的ip地址列表里面
这是觉需要去利用那些带有子网掩码的ip地址了,
/**
* 判断目标ip是否属于某个ip子网
* @param subIp 带有子网掩码的ip
* @param ip 目标ip
* @return 成功true,失败false
*/
private static boolean isSubnet(String subIp, String ip) {
String[] subs = subIp.split("/");
int[] subnetMask = getSubnetMask(Integer.parseInt(subs[1]));
int[] ipArray = getIpv4Array(subs[0]);
String subnet = getSubnet(subnetMask,ipArray);
int[] ipArray2 = getIpv4Array(ip);
String subnet2 = getSubnet(subnetMask, ipArray2);
if (subnet.equals(subnet2))
return true;
return false;
}
/**
* 获取子网掩码数组
* @param num 子网掩码长(暨带子网掩码的ip/后面的数字)
* @return 长度为4的int数组
*/
private static int[] getSubnetMask(int num) {
int[] masks = new int[4];
int index = num / 8;
int remainder = num % 8;
String remainderToBinary = "";
for (int i = 0; i < index; i++)
masks[i] = 255;
for (int i = 0; i < 8; i++) {
int j = 0;
if (i < remainder)
j = 1;
remainderToBinary = remainderToBinary + j;
}
int sublast = Integer.valueOf(remainderToBinary, 2);
masks[index] = sublast;
if (index < masks.length - 1) {
for (int i = index + 1; i < masks.length; i++)
masks[i] = 0;
}
return masks;
}
/**
* 分解ip地址
* @param ip ip地址
* @return 长度为4的int数组
*/
private static int[] getIpv4Array(String ip) {
int[] ipArray = new int[4];
String[] ips = ip.split("\\.");
for (int i = 0; i < ips.length; i++)
ipArray[i] = Integer.parseInt(ips[i]);
return ipArray;
}
/**
* 通过子网掩码数组和,ip地址数组获取主机地址
* @param masks 子网掩码数组
* @param ipArray ip数组
* @return 主机地址
*/
private static String getSubnet(int[] masks, int[] ipArray) {
String subnet = "";
if (masks.length != ipArray.length) {
try {
throw new Exception("子网掩码长度和ip地址长度不一样");
} catch (Exception e) {
e.printStackTrace();
}
}
for (int i = 0; i < ipArray.length; i++) {
int and = masks[i] & ipArray[i];
subnet = subnet + and;
if (i < ipArray.length -1)
subnet = subnet + ".";
}
return subnet;
}
所以,那些带掩码的ip,代表的是在该主机下的所有地址,/25,表示有128个地址,这时就需要计算来得到,而不是简单的字符串匹配!
企业号的就号很多,全部是.*,所以只需要判断前3个数字就可以了!