使用华为4G路由器(B311As-853)来做私人短信服务器
这台B311As-853路由器买来也快2年了吧,一直把它作为私有云的应急网络使用,里面插着一张联通卡,当家里异常停电的时候路由器就会将网络出口切换到这台4G路由器,以维持个人服务器与私有云的通信,确保随时能访问到。
这台路由器提供了短信发送服务,但是每次都要登录路由器,这是不是麻烦了点,如果能接口发送那该多好。
(English version translate by GPT-3.5)
前言
我多次尝试后,发现路由器只能通过与它相连的设备进行登录操作,如果通过nginx反代路由器管理地址然后请求nginx的地址,token获取就会报错(我没时间折腾),所以如果希望提供短信服务,就需要一台与路由器相连的设备例如树莓派啥的。我用的是B311As-853路由器,如下这一台,以及它管理界面如下。
思路
思路其实很简单,我之前不是写过一篇 使用Java编写程序来登录华为WS5200路由器 么,思路基本上与这个差不多,就是找到发送短信接口,然后模拟登录路由器,模拟发送短信,只不过这台B311As-853路由器它登录和请求的通信用的是XML。但是这个功能建议就是给自己发发,或者给自己朋友提供短信,最好别发给很多人,小心被投诉骚扰,毕竟人家是能够看到你的号码的
开始,F12起来,观察登录流程
- 首先,进入 - /html/index.html,里面内容如下- 1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 <html id="html">
 <head><head><meta name="csrf_token" content="jIe0fYkd06vlXIaEXfIboaFlAJ2TZemf">
 <meta name="csrf_token" content="fHQRMzwE5D0UoOYSDo7F3lHtlJ2vxxoi">
 <meta http-equiv="X-UA-Compatible" content="IE=edge"/>
 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
 <meta name="format-detection" content="telephone=no"/>
 <meta http-equiv='Pragma' content='no-cache'/>
 <meta http-equiv="Cache-Control" content="no-cache, must-revalidate"/>
 <meta name="description" content="emui webui 6.0"/>
 <meta name="author" content="emui webui 6.0"/>
- 其次,请求了 - /api/webserver/token,获得了一个Token- 发送 - 1 
 2
 3
 4
 <response>
 <token>kixxpBushl0g7cwfKBWo8f5x0rUQZ0343GfPcY89AmPAmGe8Rvuz7zgFgadcINsm</token>
 </response>
- 接着,仿佛带着Token的某一部分,请求 - /api/user/challenge_login- 发送头 - 1 - __RequestVerificationToken: 3GfPcY89AmPAmGe8Rvuz7zgFgadcINsm - 发送 - 1 
 2
 3
 4
 5
 6
 <request>
 <username>admin</username>
 <firstnonce>949fc48d7bd5704f508f89303ea95bec4a7902b842b70c3ff8e4a856bf2936b3</firstnonce>
 <mode>1</mode>
 </request>
 - 响应头 - 1 - __RequestVerificationToken: GvRIT0BRzfKnvh4jPJHbwEHptVKu0pN1 - 响应 - 1 
 2
 3
 4
 5
 6
 7
 8
 <response>
 <salt>ce9240656b67c976d81626aecceb3430fac98ddce1b8bb249f9fd742d0ecd01a</salt>
 <modeselected>1</modeselected>
 <servernonce>949fc48d7bd5704********SOEVzdqxyVkdOhVKODlspu</servernonce>
 <newType>0</newType>
 <iterations>1000</iterations>
 </response>
- 最后调用了 - /api/user/authentication_login来执行登录- 发送头 - 1 - __RequestVerificationToken: GvRIT0BRzfKnvh4jPJHbwEHptVKu0pN1 - 发送 - 1 
 2
 3
 4
 5
 <request>
 <clientproof>a6cba44883fd3c1efad6644d2ad4d8a03aad825e58da8e451be0494803490b5c</clientproof>
 <finalnonce>949fc48d7bd5704f508f89303ea95bec4a7902b842b70c3ff8e4a856bf2936b36rDZBBWmHpSOEVzdqxyVkdOhVKODlspu</finalnonce>
 </request>
 - 响应头 - 1 
 2
 3- __RequestVerificationToken: 60vwrcRmApKTXp23fXrMggXOKmwfaZpw#YTXJyp0GFYdE6yCRY2PXRhSePegBin0z#OxPBLp9C30mt5ATFdLhOXl7twjSg903W#Xpdf50IOyMOJ4sKiz0u4Q3WGvonhsgmB#aHs0u9wwWIUQyh8GniyAr63d0l75gvMo#ktV97hCbe1mbfBNUs0YV1r6ql5t0j5YB#5yZM9YazQzMNwFBBKLInwXgMR8QLu8wU#ggtvwsAOaU7QeuRATODQgRtyPfh0UOND#drH0yfkP078zY59OoitQPt8kd3wq6lL6#9u5P2khD8EXlbV64tfvSm5UN0KvHgZsr#7iI4fQKHpkvqVO0VLM0dmpbe8CUhRNSf#l83rMlKJPgdrUtBYzd3J4bahpvVdBpff#cARzgMu9e0IcKRjuNbq1giCFGrgHdsZJ#CueDzDma4ND0MFOWpXudq4rOe9geXCms#VZ105qsscX06ovVkV4uC4SrqxQvQD1eg#JK7Vho4MAh2aWk3Aq7bdxGXsQQIC4L8E#cMQ6I9xF0qEEdrAGPXErdW0kn9Qeg7HK#cJqgObOorgKTFlOmN0yLWSAQkvQKlcQM#4HvCvwyahOk9COjx4Iia0Hiz85h0oacZ#n0iPS1JYqo2nbat5JPfUzz3rJkn2cDzP#gnuOtEoRggG3iQOr1r8ledTG8V49RsI5#X08XP2buIl3AkkmSkZwirGvr5jvLMCRL#VqcfsbxrgoNS8CLU7IzpXbA4xkAu9kGm#00sIkIn1AdP53Jk1i7M5AshO6P7kSDlY#bytRkFGTpuEWW0zT5VIfRb52tYdp0h1o#yyNj0UBaPK9cCLZiDpYSZZD5fTSOZ25Z#9nD7B9U1tJXGUW306mD3R6d39uux69CP#8RqOYuAeV03Dk8Cs3azMuQm6X28HLBNm#08bcN2unNt4GToLJhYNch0IcRnTfxkvK#nfRmgrgDXnYjypb2GjaZrV021Lik98Vj#nYWeb59Ga6vHmGW0Yv0Vwp0cNFMkMTDz#G6rcmJnZhFTVjFxYXH7WLETJ0PY1snwk 
 __RequestVerificationTokenone: 60vwrcRmApKTXp23fXrMggXOKmwfaZpw
 __RequestVerificationTokentwo: YTXJyp0GFYdE6yCRY2PXRhSePegBin0z- 响应 - 1 
 2
 3
 4
 5
 6
 7
 <response>
 <serversignature>0289656ca121a4167f79858042119d8dca68d1615824ab59e63873f8e7041497</serversignature>
 <rsapubkeysignature>7d634a04da935fb951889f5dc71abf550028c4e0399116acd826b7115e49c1ca</rsapubkeysignature>
 <rsae>010001</rsae>
 <rsan>e75a0f874d****省略很多字*****58b2f7eb9</rsan>
 </response>
- 最后就跳到了 - /html/content.html里面了
其实整个过程跟之前WS5200路由器差不多,甚至除了xml区别没有其他的区别
一步步分出来
处理登录部分
登录部分可以看到,它基本和index.html中没有任何关系,它的第一个__RequestVerificationToken是来自token中的第32位开始,然后在每一步的操作中,从response.header.__RequestVerificationToken替换当前的token,请求index.html也只是初始化一下Cookie
| 1 | 
 | 
登录的加密方式应该和WS5200的是一致的,token获取部分就不描述了,login部分中的firstnonce,从index.js中看出,是一段长度64位的随机字符串,根据WS5200的经验,写出登录代码如下,从打印输出来看,加密方式和5200的路由器一致
| 1 | private static boolean initToken() { | 
控制台返回
| 1 | "C:\Program Files\Ja.....jar com.ruterfu.test.Huawei4GRouterAccessMain | 
继续分析短信接口流程
接下来,分析短信部分的接口,首先是登录后进入/html/content.html,这个网页部分内容是这样的
| 1 | <head><meta name="csrf_token" content="L4fvnQx0GYalJcxUQUxu70mVHpND2ZlL"> | 
然后发送短信,显示请求/api/sms/send-sms
请求头
| 1 | __RequestVerificationToken: 6FW0TNffiu2dqYo47NdoSg1T5IbcUPbp | 
请求
| 1 | 
 | 
响应头
| 1 | __RequestVerificationToken: uDlKYFpmpNbMwBQohxi0RLKhMqeOskXK | 
响应
| 1 | 
 | 
可以看到上述的token都乱了,没有什么规律,但是出现这么多次请求,应该有部分用到了token了
一个个找,从/html/content.html开始找,发现
- /api/system/onlineupg使用了一次token- 1 
 2- 请求:__RequestVerificationToken: L4fvnQx0GYalJcxUQUxu70mVHpND2ZlL 
 响应:__RequestVerificationToken: yUMKeCI7MrYZ070rLgxtwY0MbC0tLLkO
- /api/host/info使用一次token- 1 
 2- 请求:__RequestVerificationToken: cCK2EjEqAzXI8CeglGGjuIPBIUeUvzDu 
 响应:__RequestVerificationToken: kPxK0M2hRLW7cwAX1p5eoSMH1u5dWO6r
- /api/sms/sms-list-contact使用一次- 1 
 2- 请求:__RequestVerificationToken: yUMKeCI7MrYZ070rLgxtwY0MbC0tLLkO 
 响应:__RequestVerificationToken: 1g0QZRFK30Ks9AkfrNt0aT0xIAE2xTv3
- /api/sms/sms-count-contact使用一次- 1 
 2- 请求:__RequestVerificationToken: kPxK0M2hRLW7cwAX1p5eoSMH1u5dWO6r 
 响应:__RequestVerificationToken: 6FW0TNffiu2dqYo47NdoSg1T5IbcUPbp
- /api/sms/sms-list-phone使用一次- 1 
 2- 请求:__RequestVerificationToken: 1g0QZRFK30Ks9AkfrNt0aT0xIAE2xTv3 
 响应:__RequestVerificationToken: 1VbQWlAWROiyTSvEVcXMYPvlxJ010920
规律非常明显,从登录后开始,不再是上一次返回的token被下一个请求使用,而是使用上上一个token,而初始token是在content.html中。
| 1 | content.html 返回token1, token2 | 
有如上的规律,就得稍微改写下代码了
我这里用Stack,当某个请求返回了token时,将当前token pop出去,添加新的token,加上短信的代码
| 1 | public List<String> sendSms(List<String> phoneNumber, String message) { | 
上面send-status的返回内容如下
| 1 | 
 | 
测试结果
写个Main方法
| 1 | public static void main(String[] args) throws NoSuchAlgorithmException, InvalidKeySpecException, InvalidKeyException { | 
控制台输出
| 1 | Huawei init 4G router version successful! | 
手机收到的
哦别忘了写个退出方法,退出方法请求接口和参数我就不详细说了
| 1 | public static boolean logout() { | 
最后,附上完整代码
有Main方法,写个springboot接口调用下不就成了。既然都能发送短信了,那么是否能够定时从路由器去读取短信然后做自动化响应是不是也是极好的哈哈。
| 1 | package com.ruterfu.test; | 




