新年首秀,网上国网:记录一次简单部署一个node项目到服务器的过程及坑点——2022.01.09

974 阅读5分钟

前言:最近看网上国网项目跑的很火,于是扒了一个查询新老号的接口,做一次node服务的项目部署,顺便也算是练一下手吧。 查询链接:网上国网查询新老号接口 PS:没注册过的算是新号,注册过的算是老号。

一、接口逆向扒取,分析入参与返回参数

1.走一遍完整流程,找到需要的数据

1.Chrome浏览器打开上述链接,并开启控制台,切换成手机模式,并清空**netWork**选项卡,输入手机号(本人输入的手机号都是随机生成的,并不涉及任何隐私信息),点击发送验证码
2.此时,会有两种情况弹窗出现,一种是“验证码发送成功”,另一种提示是“输入的手机号已注册”
3.这时候我们看network选项卡,会出现一条新的请求,如下:

image.png

此种情况,很明显代表,该手机号码已经注册,这时候我们来看具体的抓包数据

image.png

我们主要看payload也就是负载,因为是post请求,则一定会带请求数据的,只带了一个jsonString的参数,开头是eyj...我们基本可以肯定这就是个加密了,里边一定包含了我们的手机号信息。

image.png

具体的加密入参的整个逆向过程,就不在这里多讲述了 上图已经有部分总结。我们主要关注的是t数据的构造数据过程里面的每一个字段,是干什么用的,到底会对我们的最终结果产生什么影响?这个我们一定要搞明白,不然不知道哪些参数有用,哪些参数没用就坏事了。 k7: this.encrypt.encrypt(this.refs.name.value)这个参数是最重要的,this.refs.name.value)这个参数是最重要的,this.refs.name.value这个参数在经过我们断点之后,我们就可以知道,这是我们用户输入的手机号,经过this.encrypt.encrypt这个方法加密之后,就变成了一串看不懂的字符串。

image.png

经过我们的断点进入这个方法,我们能发现,他貌似用了JSEncrypt这个前端加密库,然后用了rsa加密。我们知道,rsa加密,必须要有一个公钥publicKey的,然后后端再用私钥解密数据,去数据库查询,才能确定我们的手机号到底注册过没有。思路明确之后,我们先找一下公钥

image.png

直接搜索,我们便找到了设置公钥的方法,同样也找到了公钥。然后看代码的话,最后还对我们的数据,做了一个base64的加密,这个我们也要去还原一下,不然数据是对不上的,加密就是这俩,大体的思路还有方式都说完了,现在就该考虑如何将代码实现在node后端了。

2.来看返回数据

image.png

返回数据很明显又是一个base加密的数据,也需要我们解密才能查看

二、win系统node代码编写

如果想要实现上面的两处加密和返回数据的解密,就需要在node端实现JSEncrypt加密以及base64加解密,其中base64加解密node后端自带,现在需要找一个代替前端加密库JSEncrypt的库,可以在node上运行 直接google解决,node-jsencrypt库可以进行替代

直接贴代码,复现上述加解密实现 index.js

const express = require('express')
const bodyParser = require('body-parser');
const url = require('url');

const JSEncrypt = require('node-jsencrypt');
const axios = require('axios');
const { Buffer } = require('buffer');
const encrypt = new JSEncrypt();
//首先定义公钥
const publicKey = 'MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDNN7crxSsgp5llYivZ7javkS/ff5ux4LgDc9HiU82l9EZ4/s3mLKIr3E2+iQb9URXmGw706Nmn6u1XWaNfctTAE8RdVJXKWjIINHW/oxjJksbGfCDq4yaZUQrpW1/T+uhHVRRA0pfjVY1w4le9c0rTCci800ovr0pNdJzNuN56DQIDAQAB';
encrypt.setPublicKey(publicKey);

//设置主机名
const hostName = '0.0.0.0';
//设置端口
const port = 9001;
const app = express();
const routes = express.Router();
//设置路由访问路径和静态资源目录
app.use("", routes);
const server = app.listen(port, hostName, (err) => {
    if (err) {
        console.log(err)
    } else {
        console.log(`服务开启成功【端口号:${port}】`)
    }
});


/**
 * express建立GET接口
 * http://192.168.200.1:9000/a/ab?name='name12'&pwd='123'
 */
routes.get('/search', (req, res) => {
    //解析参数
    const params = url.parse(req.url, true).query;
    console.log("前端入参:",params)
    if (!params?.phone) {
        const responseJson = {status: 201, msg: '请检查传参,传参只能是phone',data:{}};
        res.send(responseJson)
    }
    encrypt_wsgw(params.phone).then(data=>{
        const responseJson = {status: 200, msg: '成功',data};
        res.send((responseJson));
    });

});


async function encrypt_wsgw(phone){
    //需要加密的手机号
    // const phone = '17770034789';
    const encryptedPhone = encrypt.encrypt(phone);
    // console.log('【 encryptedPhone】', encryptedPhone)

    //十位随机字符串
    let randomStr = ()=>{
        let randomStr=""
        for (var a = 0; a < 10; a++){
            randomStr += Math.floor(10 * Math.random());
        }
        return randomStr
    }
    //上级账号信息
    const urlData = {
        "k1": "42132762122",
        "k2": "f3100e73776g723019i7h0009f2ihi19",
        "k3": "620000",
        "k4": "620400",
        "k5": "620402"
    }

    //参数数据组合
    const t = {
        validateInfo: JSON.stringify({
            quInfo: {
                k7: encryptedPhone,
                accountType: "",
                businessType: "saveuser",
                sendType: "0"
            },
            uscInfo: {
                devciceId: "868800020356147",
                devciceIp: "192.168.1.1",
                member: "2203",
                tenant: "state_grid"
            }
        }),
        tCode: randomStr(),
        userInfo: JSON.stringify(urlData)
    };
    //node中base64编码
    // let buff = new Buffer(JSON.stringify(t));
    const buff = Buffer.from(JSON.stringify(t));// 默认用 utf-8 编码格式解释字符串
    const base64data = buff.toString('base64');
    // console.log('【 base64data】', base64data)
    let data = `jsonString=${base64data}`
    //需要加密发送的jsonString参数
    // console.log('【 需要加密发送的jsonString参数】', data)

    const config = {
        method: 'post',
        url: 'https://osg-static.sgcc.com.cn/osg-sfan0009/member/c7/f01',
        headers: {
            'Connection': 'keep-alive',
            'Pragma': 'no-cache',
            'Cache-Control': 'no-cache',
            'Accept': 'application/json',
            'X-Requested-With': 'XMLHttpRequest',
            'User-Agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.3 Mobile/15E148 Safari/604.1',
            'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8',
            'Origin': 'https://osg-static.sgcc.com.cn',
            'Sec-Fetch-Site': 'same-origin',
            'Sec-Fetch-Mode': 'cors',
            'Sec-Fetch-Dest': 'empty',
            'Referer': 'https://osg-static.sgcc.com.cn/activity/reg/index.html?k1=42132762122&k2=f3100e73776g723019i7h0009f2ihi19&k3=620000&k4=620400&k5=620402',
            'Accept-Language': 'zh-CN,zh;q=0.9'
        },
        data : data
    };

    async function requestSomething(){
        await axios(config)
            .then(function (response) {
                // console.log(JSON.stringify(response.data));
                //查询成功
                if (response.data.code == 1) {
                    const {returnJson} = response.data.data;
                    const buf1 = Buffer.from(returnJson, 'base64');
                    console.log('【 text】', buf1.toString())
                    return decryptData = buf1.toString()
                }
            })
            .catch(function (error) {
                console.log(error);
            });
        return decryptData
    }
    return await requestSomething()
}

package.json

{
  "name": "guowang",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo "Error: no test specified" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "axios": "^0.24.0",
    "body-parser": "^1.19.1",
    "express": "^4.17.2",
    "node-jsencrypt": "^1.0.0"
  }
}

1.端口可以自定义 2.npm i 直接安装所有依赖 3.node index.js 启动监听端口 前端或者直接api调用 拼接形式http://127.0.0.1:3001/search?phone=18770034781

三、linux服务器部署接口项目

在linux上部署最坑的是,忘记开防火墙了。对应端口的防火墙设置没有打开,导致一直请求不到对应的端口,获取不到返回数据

image.png

在自己的服务器上,查看防火墙,看一下是否添加了对应的端口规则,我就是在这被阻挡了半天,用postman死活调用不到这个api接口,最后排查,是这个端口没开放,被防火墙拦住了。引以为鉴。

部署,最好安装一个pm2 这样在你终端关掉,之后,服务也会正常使用。 npm i -g pm2 把你的项目除node_modules目录外,全部上传到你指定的目录路径,然后npm i 重装依赖 pm2 start index.js即可 要记得,执行此命令要在当前文件目录下才行。

至此,你就将整个项目从浏览器搬到了自己的服务器上,并使用node编写后端。