天天看點

JSRPC的三種實作方式

RPC 為遠端過程調用,本文通過在浏覽器端(服務端)開啟一個WebSocket服務,接收指令,執行浏覽器網頁的加密代碼,得到密文。

CMD端(用戶端)也開啟一個WebSocket服務與浏覽器端互動,通過标準輸入把指令發送給浏覽器執行。

RPC簡單實作

原測試網站

有一個base64的加密函數,是我們要調用的函數。

function encrypt(message){
    base64 = btoa(message);
    console.log(base64);
    return base64;
}
encrypt("abc");      

浏覽器本地替換:

通過浏覽器的“儲存并覆寫”本地替換功能,新增WebSocket通信用戶端代碼。

// test

function encrypt(message) {
    base64 = btoa(message);
    console.log(base64);
    return base64;
}
encrypt("abc");

(function () {
    var ws = new WebSocket("ws://127.0.0.1:5678")

        ws.onmessage = function (evt) {
        console.log("收到消息:" + evt.data);
        if (evt.data == "exit") {
            wx.close();
        } else {
            ws.send(encrypt(evt.data));
        }
    }
})()      
JSRPC的三種實作方式

Python實作websocket用戶端

import sys
import asyncio
import websockets

async def receive_message(websocket):
    while True:
        send_text = input("請輸入要加密的字元串:")
        if send_text == "exit":
            print("退出!")
            await websocket.send(send_text)
            await websocket.close()
            sys.exit()
        else:
            await websocket.send(send_text)
            response_text =await websocket.recv()
            print("\n加密結果:", response_text)
            
start_server = websockets.serve(receive_message, "127.0.0.1", 5678)
asyncio.get_event_loop().run_until_complete(start_server)
asyncio.get_event_loop().run_forever()      

最終效果:

JSRPC的三種實作方式

JsRpc的封裝:github.com/jxhczhl/JsRpc

項目位址:https://github.com/jxhczhl/JsRpc

開啟服務

go run main.go      
JSRPC的三種實作方式

浏覽器替換JS

把JsEnv.js代碼和一下代碼替換。

// 注入環境後連接配接通信
var demo = new Hlclient("ws://127.0.0.1:12080/ws?group=zzz&name=hlg");      
JSRPC的三種實作方式

執行

import requests

jscode = """
(function(){
    console.log("test")
    return "執行成功"
})()
"""

url = "http://localhost:12080/execjs"
data = {
    "group": "zzz",
    "name": "hlg",
    "jscode":jscode
}
res = requests.post(url, data=data)
print(res.text)      
JSRPC的三種實作方式

JSRPC的三種實作方式

無參擷取值

前端注入:

// 注冊一個方法 第一個參數hello為方法名,
// 第二個參數為函數,resolve裡面的值是想要的值(發送到伺服器的)
demo.regAction("hello", function (resolve) {
    //這樣每次調用就會傳回“好困啊+随機整數”
    var Js_sjz = "好困啊"+parseInt(Math.random()*1000);
    resolve(Js_sjz);
})      

請求​

​http://localhost:12080/go?group=zzz&name=hlg&action=hello​

​檢視JS執行結果:

JSRPC的三種實作方式

帶參擷取值

//寫一個傳入字元串,傳回base64值的接口(調用内置函數btoa)
demo.regAction("hello2", function (resolve,param) {
    //這樣添加了一個param參數,http接口帶上它,這裡就能獲得
    var base666 = btoa(param)
    resolve(base666);
})      
JSRPC的三種實作方式

帶多個參獲 并且使用post方式 取值

//假設有一個函數 需要傳遞兩個參數
function hlg(User,Status){
    return User+"說:"+Status;
}

demo.regAction("hello3", function (resolve,param) {
    //這裡還是param參數 param裡面的key 是先這裡寫,但到時候傳接口就必須對應的上
    res=hlg(param["user"],param["status"])
    resolve(res);
})      
JSRPC的三種實作方式

sekiro

​​說明文檔​​

建構

在Linux或者mac上,執行腳本 build_demo_server.sh,之後得到産出釋出壓縮包:sekiro-service-demo/target/sekiro-release-demo.zip

如果是windows,或者不想自己建構,可以在這裡直接​​下載下傳​​。

運作用戶端:

JSRPC的三種實作方式

注入服務端代碼

Sekiro代碼:​​https://sekiro.virjar.com/sekiro-doc/assets/sekiro_web_client.js​​

function guid() {
    function S4() {
        return (((1 + Math.random()) * 0x10000) | 0).toString(16).substring(1);
    }

    return (S4() + S4() + "-" + S4() + "-" + S4() + "-" + S4() + "-" + S4() + S4() + S4());
}

var client = new SekiroClient("ws://127.0.0.1:5620/business-demo/register?group=rpc-test&clientId=" + guid());

client.registerAction("encrypt", function(request, resolve, reject) {
    resolve(encrypt(request["data"]));
})      

這裡SekiroClient是用來與用戶端互動的類,第一個參數是WS連結,其中group是業務分組,clientId是分組中的用戶端ID。

registerAction用來注冊事件,這裡是encrypt。

request用來擷取用戶端發送來的資料,resolve用來傳回用戶端資料,通過request和resolve事件資料傳輸互動。

執行

下圖是觸發rpc-test業務分組的encrypt事件,傳輸資料abc,傳回abc的加密資料。

JSRPC的三種實作方式
(function () {
    var newElement = document.createElement("script");
    newElement.setAttribute("type", "text/javascript");
    newElement.setAttribute("src", "https://sekiro.virjar.com/sekiro-doc/assets/sekiro_web_client.js");
    document.body.appendChild(newElement);

    window.encrypt= encrypt // 設定成全局函數,避免未定義

    function guid() {
        function S4() {
            return (((1 + Math.random()) * 0x10000) | 0).toString(16).substring(1);
        }
        return (S4() + S4() + "-" + S4() + "-" + S4() + "-" + S4() + "-" + S4() + S4() + S4());
    }

    function startSekiro() {
        var client = new SekiroClient("ws://127.0.0.1:5620/business-demo/register?group=rpc-test&clientId=" + guid());

        client.registerAction("encrypt", function (request, resolve, reject) {
            resolve(window.encrypt(request["data"]));
        })
    }

    setTimeout(startSekiro, 2000) // 等待Document加載完成
})();      

參考

繼續閱讀