我是靠谱客的博主 柔弱月饼,最近开发中收集的这篇文章主要介绍Node.js授权微信公众号服务器访问本地(验证微信公众号IP地址)1    概述2    处理返回的IP地址列表3    代码的实现4    总结,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

Node.js授权微信公众号服务器访问本地(验证微信公众号IP地址)

1    概述

最近公司使用node.js进行微信公众号服务器端开发,到了接收公众号发送的事件请求并回复的模块,需要验证微信服务器地址进而增加安全性。微信公众号也为我们提供了获取IP地址列表或者IP网段信息的接口。具体文档信息如下:

 

如果公众号基于安全等考虑,需要获知微信服务器的IP地址列表,以便进行相关限制,可以通过该接口获得微信服务器IP地址列表或者IP网段信息。

 

接口调用请求说明:

请求方式:

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.2",

"101.226.103.0/25"]

}

参数说明:

ip_list:微信服务器IP地址列表

 

错误时微信会返回错误码等信息,JSON数据包示例如下(该示例为AppID无效错误):

{"errcode":40013,"errmsg":"invalid appid"}

根据微信接口得知要获取IP地址列表就需要先获取ACCESS_TOKEN(如何获取这里不做讲述,请见微信公众平台API文档关于如何获取access_token部分)。然后发送get请求到微信服务器,获取IP地址列表。

2    处理返回的IP地址列表

微信返回的IP地址列表如下:

{ ip_list:

   [ '101.226.62.77',     '101.226.62.78',     '101.226.62.79',     '101.226.62.80',     '101.226.62.81',     '101.226.62.82',     '101.226.62.83',     '101.226.62.84',     '101.226.62.85',     '101.226.62.86',     '101.226.103.59',     '101.226.103.60',     '101.226.103.61',     '101.226.103.62',     '101.226.103.63',     '101.226.103.69',     '101.226.103.70',     '101.226.103.71',     '101.226.103.72',     '101.226.103.73',     '140.207.54.73',     '140.207.54.74',     '140.207.54.75',     '140.207.54.76',     '140.207.54.77',     '140.207.54.78',     '140.207.54.79',     '140.207.54.80',     '182.254.11.203',     '182.254.11.202',     '182.254.11.201',     '182.254.11.200',     '182.254.11.199',     '182.254.11.198',     '59.37.97.100',     '59.37.97.101',     '59.37.97.102',     '59.37.97.103',     '59.37.97.104',     '59.37.97.105',     '59.37.97.106',     '59.37.97.107',     '59.37.97.108',     '59.37.97.109',     '59.37.97.110',     '59.37.97.111',     '59.37.97.112',     '59.37.97.113',     '59.37.97.114',     '59.37.97.115',     '59.37.97.116',     '59.37.97.117',     '59.37.97.118',     '112.90.78.158',     '112.90.78.159',     '112.90.78.160',     '112.90.78.161',     '112.90.78.162',     '112.90.78.163',     '112.90.78.164',     '112.90.78.165',     '112.90.78.166',     '112.90.78.167',     '140.207.54.19',     '140.207.54.76',     '140.207.54.77',     '140.207.54.78',     '140.207.54.79',     '140.207.54.80',     '180.163.15.149',     '180.163.15.151',     '180.163.15.152',     '180.163.15.153',     '180.163.15.154',     '180.163.15.155',     '180.163.15.156',     '180.163.15.157',     '180.163.15.158',     '180.163.15.159',     '180.163.15.160',     '180.163.15.161',     '180.163.15.162',     '180.163.15.163',     '180.163.15.164',     '180.163.15.165',     '180.163.15.166',     '180.163.15.167',     '180.163.15.168',     '180.163.15.169',     '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' ] }

获取到IP地址列表后进行比对就行了,那么如何获取请求的真实IP地址呢?

2.1   获取请求的真实IP地址

首先我们使用方法util.inspect(req.headers)打印出请求的头端所有信息如下:

{

 'user-agent': 'Mozilla/4.0',

  accept: '*/*',

  host: 'www.xxxxxx.cn',

  pragma: 'no-cache',

  'content-length': '346',

  'content-type': 'text/xml',

  'x-real-ip': '103.7.30.68',

  'x-forwarded-for': '103.7.30.68, 218.145.64.217',

  proxyagent: 'oray phfw 22057'

}

其中” x-real-ip”属性就是真实的请求IP了,“x-forwarded-for”代理服务器IP,其他不做概述。使用req.header('x-real-ip')或者req.headers['x-real-ip']都可以获取到IP地址的字符串。

那么这时就用获取到的请求IP地址,和微信给我们提供的IP地址列表,进行逐一比对不就可以了么?答案是否定的。我们获取到的IP地址是:103.7.30.68然而微信返回给我们的IP地址列表里没有这个IP,但是有一个挺像的IP:103.7.30.64/26。它们有什么关系么?肯定有关系,下面我就来阐述它们的关系和如何判断。

细心的朋友可能会发现,微信为我们提供的IP地址分成了两部分,一部分是正常的IP(101.226.62.77),另外一部分是带有掩码的IP地址(103.7.30.64/26)。

2.2   什么是带掩码的IP地址

对于网络IP地址的划分非常熟悉的朋友请略过这一部分,我也是刚刚学习完成,做一下笔记。

2.2.1   IP地址

IP地址是由32位(bit)的二进制数字组成。那么划分的时候就是从032~232个主机(43个亿)。由于运算过于复杂所以划分成了4个小节,使用“.”作为区分。这样便于计算。

IP地址我们一般划分为A、B、C、D、E,5类。

类别

十进制

二进制

 

起始

结束

起始

结束

解释

主类

0.0.0.1

0.255.255.254

00000000. 00000000. 00000000. 00000001

00000000.11111111.11111111.11111110

用来区分分类的IP地址

A类

1.0.0.1

126.255.255.254

00000001. 00000000. 00000000. 00000001

01111110. 11111111. 11111111. 11111110

A类IP地址主要用于政府机关

A

127.0.0.1

127.255.255.254

01111111. 00000000. 00000000. 00000001

01111111. 11111111. 11111111. 11111110

特殊的A类,为操作系统保留,又叫做环回地址。

B类

128.0.0.1

191.255.255.254

10000000. 00000000. 00000000. 00000001

10111111. 11111111. 11111111. 11111110

B类作为大型企业使用

C类

192.0.0.1

223.255.255.254

11000000. 00000000. 00000000. 00000001

11011111. 11111111. 11111111. 11111110

C类为个人用户使用

D类

224.0.0.1

239.255.255.254

11100000. 00000000. 00000000. 00000001

11101111. 11111111. 11111111. 11111110

D类用于组播,路由协议

E类

240.0.0.1

255.255.255.254

11110000. 00000000. 00000000. 00000001

11111111. 11111111. 11111111. 11111110

E类用于科研

2.2.2   子网掩码

什么是子网掩码?子网掩码又可以叫做子网掩盖码,从字面的意思来理解就是,用于区分哪些网络是子网的,只要没被遮住的部分组合起来就是一个网络。

百度的解释是子网掩码(subnet mask)又叫网络掩码、地址掩码、子网络遮罩,它是一种用来指明一个IP地址的哪些位标识的是主机所在的子网,以及哪些位标识的是主机的位掩码。子网掩码不能单独存在,它必须结合IP地址一起使用。子网掩码只有一个作用,就是将某个IP地址划分成网络地址和主机地址两部分。子网掩码的1必须是连续的。例如:

IP地址:       192.        168.        0.            0

子网掩码:    255.        255.        255.        0

那么它的IP地址网络位是:192.168.0.0

主机位是:1~254(0和255预留广播位)

准换成2进制:

IP地址:       11000000. 10101000. 00000000. 00000000       192.168.0.0

子网掩码:    11111111. 11111111. 11111111. 00000000       255.255.255.0

那么红色的是网络部分,黑色的是主机部分。这样,区分后的黑色部分IP地址就属于一个段内,就可以互相访问了。我们一般这样表示:192.168.0.1/24。

24代表了:有多少个1将IP地址掩盖住,盖住的部分属于一个网段。

那么如果换成192.168.0.1/25呢?

 

例如一(分成2个网段)192.168.0.1/25就是有25个1进行掩盖。:

IP地址:       11000000.10101000.00000000.00000001          192.168.0.1

子网掩码:    11111111.11111111.11111111.10000000          255.255.255.128

这样的IP地址和子网掩码代表了以10000000位分界线进行分割,将192.168.0.0段分成了2段网络。

从    11000000.10101000.00000000.00000001(192.168.0.1)到

11000000.10101000.00000000.01111111(192.168.0.127)属于一个网段。

       从    11000000.10101000.00000000.10000000(192.168.0.128)到

11000000.10101000.00000000.11111110(192.168.0.254)属于另外一个网段。

一个段的两头不能使用,被系统占据作为广播。也就是说192.168.0.1~192.168.0.126属于一个网段,可以正常使用。192.168.0.129~192.168.0.254属于一个网段,能够正常使用。

 

       例如二(分成4个网段)192.168.0.1/26就是有26个1进行掩盖:

       IP地址:       11000000.10101000.00000000.00000001          192.168.0.1

       子网掩码:    11111111.11111111.11111111.11000000          255.255.255.192

       这样的子网掩码把最后一个分段遮住了2位,代表了以11000000为分界线进行分割,将192.168.0.0段分成了4段网络。

从    11000000.10101000.00000000.00000001(192.168.0.1)到

              11000000.10101000.00000000.00111111(192.168.0.63)

       从    11000000.10101000.00000000.01000000(192.168.0.64)到

              11000000.10101000.00000000.01111111(192.168.0.127)

       从    11000000.10101000.00000000.10000000(192.168.0.128)到

              11000000.10101000.00000000.10111111(192.168.0.191)

       从    11000000.10101000.00000000.11000000(192.168.0.192)到

              11000000.10101000.00000000.11111110(192.168.0.254)

       一个网络的两头不能使用,被系统占据,所以这个例子中能使用的网络地址有:

192.168.0.1~192.168.0.62;

192.168.0.65~192.168.0.126;

192.168.0.129~192.168.0.190;

192.168.0.193~192.168.0.254。

       从以上两个例子中不难看出子网掩码的作用。现在我们回到微信给我们带子网掩码的IP地址意义何在?

2.3   分析微信返回带掩码的IP地址

好了,我们知道了微信返回给我们的IP地址为什么后面要跟一个/26了,那么就把刚才的请求拿出来进行分析。

微信给我们返回回来的地址列表里是这样的:103.7.30.64/26。使用刚才的方法进行拆分得出IP地址。

IP地址:       01100111.00000111.00011110.01000000          103.7.30.64

子网掩码:    11111111.11111111.11111111.11000000          255.255.255.192

从    01100111.00000111.00011110.00000000   (103.7.30.0)到

       01100111.00000111.00011110.00111111   (103.7.30.63)

从    01100111.00000111.00011110.01000000   (103.7.30.64)到

       01100111.00000111.00011110.01111111   (103.7.30.127)

从    01100111.00000111.00011110.10000000   (103.7.30.128)到

       01100111.00000111.00011110.10111111   (103.7.30.191)

从    01100111.00000111.00011110.11000000   (103.7.30.192)到

       01100111.00000111.00011110.11111111   (103.7.30.255)

所以能够使用的网络有:

103.7.30.1~103.7.30.62

103.7.30.65~103.7.30.126

103.7.30.129~103.7.30.190

103.7.30.193~103.7.30.254

那么微信发给我们的地址是103.7.30.64/26代表的是一个网络,只要是103.7.30.65~103.7.30.126都是微信的服务器IP地址。

得到结果请求IP为103.7.30.68属于103.7.30.65~103.7.30.126范畴之中,所以请求是微信服务器发送过来的。

3    代码的实现

上面说了那么多都是理论上的东西,实际体现到代码上与刚才阐述的理论还有些不同,具体如何实现请看下面:

3.1   得到微信服务器和请求地址的与运算

刚才已经看到了IP地址列表为:103.7.30.64/26。将他转换为2进制:

IP地址:       01100111.00000111.00011110.01000000          103.7.30.64/26

子网掩码:    11111111.11111111.11111111.11000000          255.255.255.192

将它进行“与”运算(“与”运算又叫AND运算,1与1得1,其他情况都的0):

              01100111.00000111.00011110.01000000          103.7.30.64/26

与运算     11111111.11111111.11111111.11000000          255.255.255.192

              01100111.00000111.00011110.01000000          103.7.30.64

请求的真实IP地址是:103.7.30.68那么它的子网掩码也是26。

              01100111.00000111.00011110.01000100          103.7.30.68/26

与运算     11111111.11111111.11111111.11000000          255.255.255.192

              01100111.00000111.00011110.01000000          103.7.30.64

它们两个结果是相等的,那么请求就是来自于微信服务器。

3.2   代码的实现

3.2.1   Node.js的实现

/**

 * 用于判断发送的请求是否来自于微信服务器。

 * 这个方法主要是验证微信服务器带子网掩码的IP地址

 * 判断目标ip是否属于某个ip子网

 * subIp: 带有子网掩码的ip

 * ip:目标ip

 */

exports.isSubnet = function(subIp, ip) {

    let subs = subIp.split("/");

    let subnetMask = getSubnetMask(parseInt(subs[1]));

    let ipArray = getIpv4Array(subs[0]);

    let subnet = getSubnet(subnetMask, ipArray);

    let ipArray2 = getIpv4Array(ip);

    let subnet2 = getSubnet(subnetMask, ipArray2);

    if (subnet == subnet2)

        return true;

    return false;

};

/**

 * 通过子网掩码数组和ip地址数组获取主机地址

 */

function getSubnet(masks, ipArray) {

    let subnet = "";

    if (masks.length != ipArray.length) {

        console.log("子网掩码长度和ip地址长度不一样");

    }

    for (let i =0; i < ipArray.length; i++) {

        let and = masks[i] & ipArray[i];

        subnet = subnet + and;

        if (i < ipArray.length - 1)

            subnet = subnet + ".";

    }

    return subnet;

};

 

/**

 * 获取子网掩码数组

 */

function getSubnetMask(num) {

    let masks = new Array(4);

    let index = parseInt(num / 8);

    let remainder = num % 8;

    let remainderToBinary = "";

    for (let i =0; i < index; i++)

        masks[i] = 255;

    for (let i =0; i < 8; i++) {

        let j = 0;

        if (i < remainder)

            j = 1;

        remainderToBinary = remainderToBinary + j;

    }

    let sublast = parseInt(remainderToBinary, 2);

    masks[index] = sublast;

    if (index < masks.length - 1) {

        for (let i = index + 1; i < masks.length; i++) {

            masks[i] = 0;

        }

    }

    return masks;

};

 

/**

 * 分解ip地址

 */

function getIpv4Array(ip) {

    let ipArray = newArray(4);

    let ips = ip.split(".");

    for (let i =0; i < ips.length; i++) {

        ipArray[i] = parseInt(ips[i]);

    }

    return ipArray;

};

只需要调用isSubnet方法,将两个IP地址传入就可以了。

3.2.2   JAVA的实现

首先非常感谢“雪谷里的暖风”(https://blog.csdn.net/rilaohn),我是通过看他的JAVA代码理解的地址计算,然后通过学习网络地址转换写出的这篇文章(有点懒,COPY他的代码变量名和方法名都没换)。非常感谢!!!

package testProject;

import java.util.Arrays;

public class test {

         public static void main(String[] args) {

                   booleanblisSubnet("103.7.30.64/26","103.7.30.68");

                   System.out.println(bl);

         }

         /** 

     * 判断目标ip是否属于某个ip子网 

     * @param subIp    带有子网掩码的ip 

     * @param ip       目标ip 

     * @return         成功true,失败false 

     */ 

    private static boolean isSubnet(StringsubIp, String ip) { 

        String[] subs = subIp.split("/"); 

        System.out.println("subs:"+Arrays.toString(subs));

        int[] subnetMask = getSubnetMask(Integer.parseInt(subs[1]));

        System.out.println("subnetMask:"+Arrays.toString(subnetMask));

        int[] ipArray = getIpv4Array(subs[0]);

        System.out.println("ipArray:"+Arrays.toString(ipArray));

        String subnet = getSubnet(subnetMask,ipArray);

        System.out.println("subnet:"+subnet);

        int[] ipArray2 = getIpv4Array(ip);

        System.out.println("ipArray2:"+Arrays.toString(ipArray2));

        String subnet2 = getSubnet(subnetMask,ipArray2);

        System.out.println("subnet2:"+subnet2);

        if (subnet.equals(subnet2))

            return true

        return false

    } 

      /** 

     * 获取子网掩码数组 

     * @param num  子网掩码长(暨带子网掩码的ip/后面的数字) 

     * @return     长度为4的int数组 

     */ 

    private static int[] getSubnetMask(intnum) { 

        int[] masks = new int[4]; 

        int index = num / 8; 

        int remainder = num % 8; 

        String remainderToBinary = ""

        for (inti = 0; i < index; i++)  //循环3

            masks[i] = 255; 

        for (inti = 0; i < 8;i++) { 

            int j = 0; 

            if (i <remainder

                j = 1; 

            remainderToBinary = remainderToBinary + j

        }

       

        int sublast = Integer.valueOf(remainderToBinary, 2);

        masks[index] =sublast;

        System.out.println("masks:"+Arrays.toString(masks));

        if (index <masks.length - 1) { 

            for (inti = index + 1;i < masks.length;i++) 

                masks[i] = 0; 

        } 

        return masks

    } 

     /** 

     * 分解ip地址 

     * @param ip    ip地址 

     * @return     长度为4的int数组 

     */ 

    private static int[] getIpv4Array(Stringip) { 

        int[] ipArray = new int[4]; 

        String[] ips = ip.split("\."); 

        for (inti = 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 (Exceptione) { 

                e.printStackTrace(); 

            } 

        } 

        for (inti = 0; i < ipArray.length; i++) { 

            int and = masks[i] &ipArray[i]; 

            subnet = subnet + and

            if (i <ipArray.length -1)  

                subnet = subnet + "."

        } 

        return subnet

    } 

}

4    总结

好了,今天的地址转换就写到这里了。这里牵扯的技术点估计以后用的时候并不会多,放到这里供大家学习,也为以后自己再用的时候能立马翻出来掌握。如果有错的地方还请大家指正。


最后

以上就是柔弱月饼为你收集整理的Node.js授权微信公众号服务器访问本地(验证微信公众号IP地址)1    概述2    处理返回的IP地址列表3    代码的实现4    总结的全部内容,希望文章能够帮你解决Node.js授权微信公众号服务器访问本地(验证微信公众号IP地址)1    概述2    处理返回的IP地址列表3    代码的实现4    总结所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(41)

评论列表共有 0 条评论

立即
投稿
返回
顶部