spiderdemo第22题与webassembly的跨域

目录

前言

正文

前置分析

继续分析

建立wasm项目

前置准备

编写爬虫

加密参数

请求头

发送请求的函数

跨域

反向代理

试试登录与退出

登录

退出

全部代码如下

Cargo.toml

lib.rs

index.html

cors.py

总结


前言

直言的说,这道题很简单,但笔者有其他好玩的想法

笔者决定使用rust来写爬虫,而且笔者想使用wasm,在wasm里面发送请求。

(0.0)(0.0)(0.0)(0.0)(0.0)(0.0)

正文

页面如下

T22-对称加密https://www.spiderdemo.cn/authentication/symmetry_challenge/?challenge_type=symmetry_challenge

前置分析

多次点击下一页,观察参数和请求头,可以发现有四个参数需要加密,如下

直接搜关键字,比如X-Aes-Token,就可以发现关键信息

可以发现是在symmetry_challenge.js文件中,下载下来,继续分析看看

继续分析

给出关键代码

javascript 复制代码
 
        var n, t, r, o = e.url.match(/\/page\/(\d+)\//);
        if (o) {
            var a = parseInt(o[1])
              , c = new URLSearchParams(e.url.split("?")[1] || "").get("challenge_type") || "symmetry_challenge"
              , s = Date.now()
              , i = "".concat(a, "_").concat(c, "_").concat(s);
            e.headers["X-Aes-Token"] = (n = i,
            t = CryptoJS.enc.Utf8.parse("1234567890123456"),
            r = CryptoJS.enc.Utf8.parse(u),
            CryptoJS.AES.encrypt(n, t, {
                iv: r,
                mode: CryptoJS.mode.CTR,
                padding: CryptoJS.pad.NoPadding
            }).toString()),
            e.headers["X-Des-Token"] = l(i);
            var p = function(e) {
                var n = CryptoJS.enc.Utf8.parse("12345678901234567890123456789012")
                  , t = CryptoJS.enc.Utf8.parse(u);
                return CryptoJS.AES.encrypt(e, n, {
                    iv: t,
                    mode: CryptoJS.mode.OFB,
                    padding: CryptoJS.pad.NoPadding
                }).toString()
            }(i)
              , f = l(i + "_param")
              , d = e.url.includes("?") ? "&" : "?";
            e.url += "".concat(d, "aes_sign=").concat(encodeURIComponent(p), "&des_sign=").concat(encodeURIComponent(f), "&t=").concat(s)
        }
        
    

加密的方法是很显然的,分别是AES加密和DES加密,相关参考如下

【密码学】DES算法和AES算法(Rijndael算法)数学原理及实现 - stackupdown - 博客园https://www.cnblogs.com/wangzming/p/7991322.htmlJS实现AES和DES_des js-CSDN博客https://blog.csdn.net/qq_39706570/article/details/147025994#:~:text=%E4%BA%86%E8%A7%A3%20AES%20%E5%92%8CDES%E7%9A%84%E7%89%B9%E7%82%B9%E5%B9%B6%E7%94%A8JS%E5%AE%9E%E7%8E%B0%E3%80%82%20%E7%BF%BB%E8%AF%91%20%E8%BF%87%E6%9D%A5%E5%8F%AB%20%E6%95%B0%E6%8D%AE%E5%8A%A0%E5%AF%86%E6%A0%87%E5%87%86%E3%80%82,%E5%AE%83%E6%9C%895%E7%A7%8D%E5%8A%A0%E5%AF%86%E6%A8%A1%E5%BC%8F%EF%BC%88CTR%E3%80%81OFB%E3%80%81CFB%E3%80%81CBC%E3%80%81ECB%EF%BC%89%EF%BC%8C%E5%9C%A8JS%E4%B8%AD%EF%BC%8C%E4%B8%8D%E5%90%8C%E5%8A%A0%E5%AF%86%E6%A8%A1%E5%BC%8F%E8%AF%AD%E6%B3%95%E7%BB%93%E6%9E%84%E5%87%A0%E4%B9%8E%E4%B8%80%E8%87%B4%EF%BC%8C%E4%B8%BB%E8%A6%81%E5%8C%BA%E5%88%AB%E5%B0%B1%E6%98%AFmode%E8%AE%BE%E7%BD%AE%E5%92%8C%E6%98%AF%E5%90%A6%E9%9C%80%E8%A6%81iv%EF%BC%88%E5%88%9D%E5%A7%8B%E5%8C%96%20%E5%90%91%E9%87%8F%EF%BC%8C%E5%8F%AF%E4%BB%A5%E7%90%86%E8%A7%A3%E4%B8%BA%E7%AC%AC%E4%BA%8C%E4%B8%AA%E5%AF%86%E9%92%A5%EF%BC%89%EF%BC%8C%E5%85%B6%E4%B8%AD%20ECB%E4%B8%8D%E9%9C%80%E8%A6%81iv%EF%BC%8C%E5%85%B6%E4%BB%96%E6%A8%A1%E5%BC%8F%E9%83%BD%E9%9C%80%E8%A6%81%E3%80%82%20%E5%AE%83%E5%85%B7%E6%9C%89%E4%BB%A5%E4%B8%8B%E7%89%B9%E7%82%B9%EF%BC%9A%20%E5%AF%86%E9%92%A5%E9%95%BF%E5%BA%A6%EF%BC%9A56%E4%BD%8D%EF%BC%88%E5%AE%9E%E9%99%85%E4%B8%8A%E6%98%AF64%E4%BD%8D%EF%BC%8C%E4%BD%86%E6%AF%8F8%E4%BD%8D%E4%B8%AD%E6%9C%891%E4%BD%8D%E7%94%A8%E4%BA%8E%E5%A5%87%E5%81%B6%E6%A0%A1%E9%AA%8C%EF%BC%89%E3%80%82%20%E5%88%86%E7%BB%84%E5%8A%A0%E5%AF%86%EF%BC%9A64%E4%BD%8D%E5%88%86%E7%BB%84%E3%80%82二者都是对称加密。

看代码

首先,先对url里面进行了正则匹配,获取了n,t,r,o

然后,if判断o是否为true, 如果存在,把o解析成int,

说白了a就是page,是一个int类型的


c------是一个定值symmetry_challenge

s是13为时间戳

i是a,c,s通过下划线拼接二次的,


X-Aes-Token使用的是Aes加密,密钥key是1234567890123456,被解析成CryptoJS里面的类型是WordArray

偏移量iv是通过u解析来的,而u是一个定值,如下

使用的模式是CTR,填充方式是NoPadding------不进行任何填充。


X-Des-Token 调用l函数

如下

javascript 复制代码
    function l(e) {
        var n = CryptoJS.enc.Utf8.parse("6f726c64"), t = CryptoJS.enc.Utf8.parse("01234567");
        return CryptoJS.DES.encrypt(e, n, {iv: t, mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7}).toString()
    }

类似的。


aes_sign和des_sign也是类似都,调用对应的加密方法,各自的密钥。


总之,没有js混淆那些,加密是很显然的。

建立wasm项目

笔者使用rust,需要安装一个工具

cargo install wasm-pack

安装完成后,初始化一个wasm项目

wasm-pack new wasm-app

删除一下没有用的东西,添加爬虫需要的依赖,关键的Cargo.toml文件如下

javascript 复制代码
[package]
name = "wasm-app"
version = "0.1.0"
edition = "2024"

[lib]
crate-type = ["cdylib"]


[dependencies]
wasm-bindgen = "0.2.105"

前置准备

新建一个index.html

文件目录如下

其中index.html,暂时的内容如下

javascript 复制代码
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.1.1/crypto-js.min.js"></script>
    <script>
        console.log(Crypto)
    </script>
</head>
<body>

</body>
</html>

导入了加密库,顺便打印一下

没有报错,没问题


====明天再说=======


题外话,笔者先安装一个工具

cargo install cargo-make

cargo-make rust 任务执行以及构建工具 - 荣锋亮 - 博客园https://www.cnblogs.com/rongfengliang/p/17910340.html为了后面构建。


继续,在wasm-z目录下新建一个main.js文件,结合前面的分析,main.js文件的内容如下

javascript 复制代码
function l(e) {
    var n = CryptoJS.enc.Utf8.parse("6f726c64"), t = CryptoJS.enc.Utf8.parse("01234567");
    return CryptoJS.DES.encrypt(e, n, {iv: t, mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7}).toString()
}
export function get_params(page) {
    let n, t, r;
    let c = "symmetry_challenge"
    let s = Date.now()
    let i = "".concat(page, "_").concat(c, "_").concat(s)
    let u = "abcdefghijklmnop"
    let aes_token = (n = i,
        t = CryptoJS.enc.Utf8.parse("1234567890123456"),
        r = CryptoJS.enc.Utf8.parse(u),
        CryptoJS.AES.encrypt(n, t, {
            iv: r,
            mode: CryptoJS.mode.CTR,
            padding: CryptoJS.pad.NoPadding
        }).toString())
    let des_token = l(i)
    let aes_sign = function (e) {
        n = CryptoJS.enc.Utf8.parse("12345678901234567890123456789012")
        t = CryptoJS.enc.Utf8.parse(u);
        return CryptoJS.AES.encrypt(e, n, {
            iv: t,
            mode: CryptoJS.mode.OFB,
            padding: CryptoJS.pad.NoPadding
        }).toString()
    }(i)
    let des_sign = l(i + "_param")
    return {
        aes_token: aes_token,
        des_token: des_token,
        aes_sign: aes_sign,
        des_sign: des_sign,
        timestamp: s.toString(),
    }
}

不需要那个symmetry_challenge.js文件了

这个main.js文件就是用来加密的,可以在wasm项目中导入

在wasm-app/src/lib.rs文件的内容如下

javascript 复制代码
use wasm_bindgen::prelude::*;
use reqwest::Client;
use web_sys::console::log_1;

#[wasm_bindgen(module="/main.js")]
extern "C" {
    #[wasm_bindgen(js_name = get_params)]
    fn get_params_js(page: u32) -> JsValue;
}


#[wasm_bindgen]
pub fn get_request(page:u32)->u32{
    let a=get_params_js(1);
    // 打印看看
    log_1(&a.into());
    1
}

暂时打包

如果使用cargo make

则Makefile.toml文件的内容如下

[tasks.build-wasm]
command = "wasm-pack"
args = ["build", "--target", "web", "--out-dir", "../t22/pkg", "--no-pack"]

或者

直接打包

wasm-pack build

笔者打包后,目录如下

在index.html中引入js文件

javascript 复制代码
    <script type="module">
        import init,{get_request} from "./pkg/wasm_app.js";
        init().then(() => {
            let a=get_request(1);
            console.log(a)
        });
    </script>

运行index.html

输出如下

可以看到生成了参数,好

编写爬虫

慢慢来,首先,写请求头

加密参数

前面通过js文件获取加密后的数据,这个数据的类型是JsValue,需要将其变成Rust的类型------结构体。还与wasm有关,因此,需要新的crate

serde = {version = "1", features = ["derive"]}
serde-wasm-bindgen = {version = "0.6"}

考虑参数,aes、des分别两个,还有一个时间戳

因此,结构体定义如下

javascript 复制代码
#[derive(Debug, Deserialize)]
struct Params {
    aes_token: String,
    des_token: String,
    aes_sign: String,
    des_sign: String,
    timestamp: String,
}

把JsValue变成Params,需要使用serde-wasm-bindgen

rust 复制代码
use serde_wasm_bindgen::from_value;
let params = from_value::<Params>(a).unwrap();

当然,如果要打印params到控制台上,可以变成字符串,即

rust 复制代码
log_1(&JsValue::from_str(&format!("{:?}", params)));

结果如下

也可以为Params实现**From这个trait。**代码如下

rust 复制代码
impl From<JsValue> for Params {
    fn from(v: JsValue) -> Self {
        from_value(v).unwrap()
    }

}

实现了From,代码如下

rust 复制代码
let params = Params::from(a);

差不多。

如果反过来,Params变成JsValue,这其实很简单,实现Serialize就可以

代码如下

rust 复制代码
#[derive(Debug, Deserialize, Serialize)]
struct Params {
    aes_token: String,
    des_token: String,
    aes_sign: String,
    des_sign: String,
    timestamp: String,
}
log_1(&to_value(&params).unwrap());

打印结果如下

可以发现前面把params的打印是字符串,而实现Serialize后的打印是JS对象。

还是有点区别的。

请求头

直接给出代码

javascript 复制代码
fn headers(aes_token: String, des_token: String) -> HeaderMap {
    let mut headers = HeaderMap::new();
    headers.insert(CONTENT_TYPE, "application/json".parse().unwrap());
    headers.insert("Cookie",HeaderValue::from_static("xxxxxxx"));
    headers.insert("X-Aes-Token", HeaderValue::from_str(&aes_token).unwrap());
    headers.insert("X-Des-Token", HeaderValue::from_str(&des_token).unwrap());
    headers
}

需要三个参数。

发送请求的函数

直接给出代码

rust 复制代码
#[derive(Serialize)]
struct Query {
    challenge_type: String,
    aes_sign: String,
    des_sign: String,
    t: String,
}
async fn get_response(page: u32, params: Params) -> String {
    let url = format!(
        "https://www.spiderdemo.cn/authentication/api/symmetry_challenge/page/{}/",
        page
    );
    let request = Client::new();
    let query = Query {
        challenge_type: "symmetry_challenge".to_string(),
        aes_sign: params.aes_sign.clone(),
        des_sign: params.des_sign.clone(),
        t: params.timestamp.clone(),
    };
    let headers = headers(params.aes_token, params.des_token);
    let res=request
        .get(url)
        .query(&query)
        .headers(headers)
        .send()
        .await
        .map_err(|err| log_1(&JsValue::from_str(&err.to_string())))
        .unwrap()
        .text()
        .await
        .unwrap();
    log_1(&JsValue::from_str(&res));
    res
}

感觉有点重复,算了

最终的调用

rust 复制代码
#[wasm_bindgen]
pub async fn get_request(page: u32) -> u32 {
    let a = get_params_js(1);
    let params = Params::from(a);
    log_1(&to_value(&params).unwrap());
    get_response(page, params).await;
    1
}

调用get_requests

rust 复制代码
    <script type="module">
        import init,{get_request} from "./pkg/wasm_app.js";
        init().then(() => {
           get_request(1).then((data)=>{
                console.log(data);
            });

        });
    </script>

然后,结果如下

index.html?_ijt=3nbld9mcoqmquitkecmlltr0al&_ij_reload=RELOAD_ON_SAVE:1 Access to fetch at 'https://www.spiderdemo.cn/authentication/api/symmetry_challenge/page/1/?challenge_type=symmetry_challenge\&aes_sign=%2BmSkBuYfTT5lH37rJl9qpeN9ZqcuSC4cEuw7ejPS58tYTQ%3D%3D\&des_sign=l39NBujH1q5MFjdmTys13Hmm2c5eDA%2B1KhqP5p9fq2yV8ezI1WAyoK%2FBd2SwRwaX\&t=1762438661611' from origin 'http://localhost:64542' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.

什么报错------跨域

跨域

笔者搜了搜,看到了wasm-bindgen官网的一个案例

web-sys:使用 fetch - `wasm-bindgen` 指南 - Rust 和 WebAssemblyhttps://wasm.rust-lang.net.cn/docs/wasm-bindgen/examples/fetch.html啊,这,笔者发现前面写的都没有用。呜呜呜呜呜呜。

写了个寂寞

然后,笔者也尝试了,

案例的方式,也不行。。。。。。。。。。。。。。。。。。。。

不是,看来服务器没有Access-Control-Allow-Origin为*

确实,笔者想的太简单了。。。。。

笔者发现这好像是wasm致命的问题。啊啊啊啊啊

Reqwest WASM跨域请求中的凭证传递问题解析 - GitCode博客https://blog.gitcode.com/a210f0fb238c5d9c8e8ebb55e79ec3b6.htmlwebassembly跨域访问_webassembly 跨域加载wasm-CSDN博客https://blog.csdn.net/henreash/article/details/108530401




笔者到处搜索,发现不行,看来只能反向代理,一条路走到黑了

mitmproxy.orghttps://www.mitmproxy.org/(30 封私信 / 86 条消息) 实战|手把手教你如何使用抓包神器MitmProxy - 知乎https://zhuanlan.zhihu.com/p/396398412

直言的说,笔者还从来没有使用过这个东西,正好学习和使用一下


=========算了,明天再来=======


反向代理

笔者看到可以使用python去下载mitmproxy,那为什么不直接用python写爬虫

给自己整笑了,算了。。。。。。。。。。。

首先,思考一下自身

  1. 笔者在wasm里面发送请求
  2. wasm文件在html里面
  3. 笔者使用的是rustrover打开的html

因此,需要知道rustrover里面打开html的端口号,如下

可以看到是63342,这个端口号可能会变,随机应变,无所谓。

  1. 需要拦截options方法,不发送到服务器,也就是不发送到spiderdemo.cn,直接回 204 No Content
  2. 还需要设置响应头,比如Access-Control-Allow-Origin之类的,已经请求头参数
  3. 以及需要cookie,需要凭证

那么,搞事情

在某个python项目中,安装依赖,新建一个cors.py文件

rust 复制代码
from mitmproxy import http

ALLOWED_ORIGIN = "http://localhost:63342"

def responseheaders(flow: http.HTTPFlow):
    flow.response.headers["Access-Control-Allow-Origin"]  = ALLOWED_ORIGIN
    flow.response.headers["Access-Control-Allow-Credentials"] = "true"
    flow.response.headers["Access-Control-Allow-Headers"] = "X-Aes-Token,X-Des-Token,Content-Type"
    flow.response.headers["Access-Control-Allow-Methods"] = "GET,POST,OPTIONS"



def request(flow: http.HTTPFlow):
    if flow.request.method == "OPTIONS":
        flow.response = http.Response.make(
            204, b"",
            {"Access-Control-Max-Age": "86400"}
        )

运行

mitmproxy --mode reverse:https://www.spiderdemo.cn@8082 -s cors.py

监听8082端口,为什么是8082,因为笔者8080和8081被占用,可以切换。

把127.0.0.1:8082代理到https://www.spiderdemo.cn

那么,修改url

rust 复制代码
    let url = format!(
        "http://127.0.0.1:8082/authentication/api/symmetry_challenge/page/{}/",
        page
    );

打包,并运行inde.html

但是结果如下

options是过去了,但是没有cookie,笔者明明设置了

但是,请求头里面没有cookie,就没有cookie,不是

为什么?????????????

笔者去学习了一下

cookie带不上?响应头拿不到数据?后端返回了cookie但是前端请求时候灭有自动带上?后端响应头返回了东西,但是前端 - 掘金https://juejin.cn/post/7363115064729059343笔者看了看,笔者明白了

  1. 需要credentials: 'include'
  2. 响应头里面设置cookie
  3. 后设置cookie时候需要需要设置这两个选项sameSite: 'none'; secure: 'false'

在reqwest里面如下设置credentials,根据前面引用的url

Reqwest WASM跨域请求中的凭证传递问题解析 - GitCode博客https://blog.gitcode.com/a210f0fb238c5d9c8e8ebb55e79ec3b6.html可以设置fetch_credentials_include,哦

cookie可以在mitmproxy里面修改

因此,代码如下

在lib.rs文件中

代码如下

rust 复制代码
async fn get_response(page: u32, params: Params) -> String {
    let url = format!(
        "http://127.0.0.1:8082/authentication/api/symmetry_challenge/page/{}/",
        page
    );
    let request = Client::builder()
        .build()
        .unwrap();
    let query = Query {
        challenge_type: "symmetry_challenge".to_string(),
        aes_sign: params.aes_sign.clone(),
        des_sign: params.des_sign.clone(),
        t: params.timestamp.clone(),
    };
    let headers = headers(params.aes_token, params.des_token);
    let res=request
        .get(url)
        .query(&query)
        .headers(headers)
        .fetch_credentials_include()
        .send()
        .await
        .map_err(|err| log_1(&JsValue::from_str(&err.to_string())))
        .unwrap()
        .text()
        .await
        .unwrap();
    log_1(&JsValue::from_str(&res));
    res
}

设置了fetch_credentials_include

在python的cors.py文件中

python 复制代码
from mitmproxy import http

ALLOWED_ORIGIN = "http://localhost:63343"

def responseheaders(flow: http.HTTPFlow):
    # 只在反向代理模式下给**响应**加头
    flow.response.headers["Access-Control-Allow-Origin"]  = ALLOWED_ORIGIN
    flow.response.headers["Access-Control-Allow-Credentials"] = "true"
    flow.response.headers["Access-Control-Allow-Headers"] = "X-Aes-Token,X-Des-Token,Content-Type"
    flow.response.headers["Access-Control-Allow-Methods"] = "GET,POST,OPTIONS"
    flow.response.headers['Set-Cookie']="sessionid=******;SameSite=None;Secure=false;"



def request(flow: http.HTTPFlow):
    if flow.request.method == "OPTIONS":
        flow.response = http.Response.make(
            204, b"",
            {"Access-Control-Max-Age": "86400"}
        )

再次打包,虽然说会报错

但是build没有问题

运行html

终于成功了,笔者搞了寂寞。等同于自己部署了项目。

既然mitmproxy可以代理,那应该也可以拦截,那么直接把这个get请求给拦截了,添加cookie不就可以了,搞了个寂寞,因此,修改一下python代码

python 复制代码
def request(flow: http.HTTPFlow):
    if flow.request.method == "OPTIONS":
        flow.response = http.Response.make(
            204, b"",
            {"Access-Control-Max-Age": "86400"}
        )
    if flow.request.method=="GET":
        flow.request.headers["Cookie"]="sessionid=xxxxx"

再次运行,如下

没有cookie,但是成功了

全部问题解决。

试试登录与退出

但是笔者想看一看登录,如下

看看响应标头里面有没有Set-Cookies

还真有,笔者发现这个cookie是会刷新的,因此,直接给出来

这就有点意思了,笔者发现还有退出

既然如下,用wasm写一个登录,mitmproxy拦截,cookie存储到mitmproxy里面,

退出的时候,cookie清除,没问题。

登录

登录成功返回的结果如下

会返回success。代码如下

rust 复制代码
#[derive(Serialize)]
struct Login {
    username: String,
    password: String,
}
#[derive(Deserialize)]
struct LoginResponse {
    success: bool,
}
#[wasm_bindgen]
pub async fn login(username: String, password: String) -> bool {
    let mut h = HeaderMap::new();
    h.insert("Content-Type", "application/json".parse().unwrap());
    let response = Client::new()
        .post("http://127.0.0.1:8082/admin_I/api/auth/login")
        .headers(h)
        .json(&Login {
            username,
            password,
        })
        .send()
        .await
        .unwrap()
        .json::<LoginResponse>();
    response.await.unwrap().success
}

退出

rust 复制代码
#[wasm_bindgen]
pub async fn logout()->bool {
    let mut h = HeaderMap::new();
    h.insert("Content-Type", "application/json".parse().unwrap());
    let response = Client::new()
        .post("http://127.0.0.1:8082/admin_I/api/auth/logout")
        .headers(h)
        .send()
        .await
        .unwrap()
        .json::<LoginResponse>();
    response.await.unwrap().success
}

很简单,不必多言。

全部代码如下

走到这里,直接给出代码

Cargo.toml

rust 复制代码
[package]
name = "wasm-app"
version = "0.1.0"
edition = "2024"

[lib]
crate-type = ["cdylib"]


[dependencies]
wasm-bindgen = "0.2.105"
serde = {version = "1", features = ["derive"]}
serde-wasm-bindgen = {version = "0.6"}
wasm-bindgen-futures = {version = "0.4"}
reqwest = {version = "0.12.24",features = ["json"]}
web-sys = { version = "0.3", features = ['console'] }

lib.rs

rust 复制代码
use reqwest::header::{HeaderMap, HeaderValue, CONTENT_TYPE};
use reqwest::Client;
use serde::{Deserialize, Serialize};
use serde_wasm_bindgen::from_value;
use wasm_bindgen::prelude::*;
use web_sys::console::log_1;

#[wasm_bindgen(module = "/main.js")]
extern "C" {
    #[wasm_bindgen(js_name = get_params)]
    fn get_params_js(page: u32) -> JsValue;
}

#[derive(Debug, Deserialize, Serialize)]
struct Params {
    aes_token: String,
    des_token: String,
    aes_sign: String,
    des_sign: String,
    timestamp: String,
}
impl From<JsValue> for Params {
    fn from(v: JsValue) -> Self {
        from_value(v).unwrap()
    }
}
fn headers(aes_token: String, des_token: String) -> HeaderMap {
    let mut headers = HeaderMap::new();
    headers.insert(CONTENT_TYPE, "application/json".parse().unwrap());
    headers.insert("X-Aes-Token", HeaderValue::from_str(&aes_token).unwrap());
    headers.insert("X-Des-Token", HeaderValue::from_str(&des_token).unwrap());
    headers
}
#[derive(Serialize)]
struct Query {
    challenge_type: String,
    aes_sign: String,
    des_sign: String,
    t: String,
}
#[derive(Deserialize)]
struct Response {
    page_data: Vec<u32>,
}
#[derive(Serialize)]
struct Login {
    username: String,
    password: String,
}
#[derive(Deserialize)]
struct LoginResponse {
    success: bool,
}
async fn get_response(page: u32, params: Params) -> u32 {
    let url = format!(
        "http://127.0.0.1:8082/authentication/api/symmetry_challenge/page/{}/",
        page
    );
    let request = Client::builder()
        .build()
        .unwrap();
    let query = Query {
        challenge_type: "symmetry_challenge".to_string(),
        aes_sign: params.aes_sign.clone(),
        des_sign: params.des_sign.clone(),
        t: params.timestamp.clone(),
    };
    let headers = headers(params.aes_token, params.des_token);
    let res = request
        .get(url)
        .query(&query)
        .headers(headers)
        .send()
        .await
        .map_err(|err| log_1(&JsValue::from_str(&err.to_string())))
        .unwrap()
        .json::<Response>()
        .await
        .unwrap();
    res.page_data.into_iter().sum()
}
#[wasm_bindgen]
pub async fn get_data(page: u32) -> u32 {
    let a = get_params_js(page);
    let params = Params::from(a);
    get_response(page, params).await
}
#[wasm_bindgen]
pub async fn login(username: String, password: String) -> bool {
    let mut h = HeaderMap::new();
    h.insert("Content-Type", "application/json".parse().unwrap());
    let response = Client::new()
        .post("http://127.0.0.1:8082/admin_I/api/auth/login")
        .headers(h)
        .json(&Login {
            username,
            password,
        })
        .send()
        .await
        .unwrap()
        .json::<LoginResponse>();
    response.await.unwrap().success
}
#[wasm_bindgen]
pub async fn logout()->bool {
    let mut h = HeaderMap::new();
    h.insert("Content-Type", "application/json".parse().unwrap());
    let response = Client::new()
        .post("http://127.0.0.1:8082/admin_I/api/auth/logout")
        .headers(h)
        .send()
        .await
        .unwrap()
        .json::<LoginResponse>();
    response.await.unwrap().success
}

index.html

html 复制代码
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.1.1/crypto-js.min.js"></script>
    <script type="module">
        import init, {get_data, login,logout} from "./pkg/wasm_app.js";
        init()
        window.getOne = async function () {
            try {
                const data = await get_data(1);
                console.log("第一页数据:", data);
            } catch (e) {
                alert("获取数据失败:" + e);
            }
        };
        window.doLogin = async function () {
            const username = document.getElementById("username").value.trim();
            const password = document.getElementById("password").value.trim();
            if (!username || !password) {
                alert("请输入用户名和密码");
                return;
            }
            try {
                const msg = await login(username, password);
                console.log("登录成功:", msg);
            } catch (e) {
                console.error("登录失败:", e);

            }
        };
        window.logoutPage=async ()=>{
            let res=await logout()
            console.log("退出成功:", res);
        };
    </script>
</head>

<body>
<input id="username" type="text" placeholder="用户名">
<input id="password" type="password" placeholder="密码">
<button onclick="doLogin()">登录</button>
<button onclick="getOne()">获取第一页数据</button>
<button onclick="logoutPage()">登出</button>
</body>
</html>

cors.py

python 复制代码
from mitmproxy import http

ALLOWED_ORIGIN = "http://localhost:63343"
COOKIE = ""


def responseheaders(flow: http.HTTPFlow):
    # 只在反向代理模式下给**响应**加头
    global COOKIE

    # 只拦截登录接口(反向代理后 URL 已经是 spiderdemo 的)
    if (
            flow.request.method == "POST"
            and flow.request.path == "/admin_I/api/auth/login"
            and flow.response
    ):
        cookies = flow.response.headers.get_all("Set-Cookie")
        COOKIE = "; ".join(c.split(";", 1)[0] for c in cookies)

    flow.response.headers["Access-Control-Allow-Origin"] = ALLOWED_ORIGIN
    flow.response.headers["Access-Control-Allow-Credentials"] = "true"
    flow.response.headers["Access-Control-Allow-Headers"] = "X-Aes-Token,X-Des-Token,Content-Type"
    flow.response.headers["Access-Control-Allow-Methods"] = "GET,POST,OPTIONS"


def request(flow: http.HTTPFlow):
    global COOKIE
    if flow.request.method == "OPTIONS":
        flow.response = http.Response.make(
            204, b"",
            {"Access-Control-Max-Age": "86400"}
        )
    if flow.request.method in ("GET", "POST"):
        flow.request.headers["Cookie"] = COOKIE

登录与退出的结果

运行,爬虫结果如下

没问题。

总结

笔者写了几天,没绷住,就问题本身就是其实很简单,没用wasm也不会遇到跨域问题,

笔者以前还不知道,正好使用一些mitmproxy这个代理,用起来感觉可以,相关参考如下

钩子签名 触发时机 典型用途 能否修改请求/响应?
def request(flow: http.HTTPFlow) 客户端→mitmproxy 刚到代理层,还未发往服务器 改请求头、改路径、直接返回假响应、丢弃请求 ✅ 可改 flow.request
def response(flow: http.HTTPFlow) 服务器→mitmproxy 刚到代理层,还未发回客户端 改响应头、改 body、存 Cookie、打日志 ✅ 可改 flow.response
def responseheaders(flow: http.HTTPFlow) 服务器→mitmproxy 仅响应头已到达,body 还在路上 只改头、不改 body,节省内存 ✅ 可改 flow.response.headers
def http_connect(flow: tcp.HTTPFlow) CONNECT 隧道建立前 拦截 HTTPS 隧道 ✅ 可丢弃
def client_connected(client: tcp.Client) TCP 三次握手完成 记录 IP、端口 ❌ 不能改 HTTP 内容

说起来,想不到还可以这么玩,哈哈哈哈哈

有点意思,以后再来试试

相关推荐
WenGyyyL2 小时前
微信小程序开发——第二章:微信小程序开发环境搭建
开发语言·python·微信小程序
循环过三天2 小时前
3.2、Python-元组
开发语言·python
一抓掉一大把3 小时前
RuoYI框架.net版本实现Redis数据隔离
java·开发语言
m0_748248023 小时前
揭开 C++ vector 底层面纱:从三指针模型到手写完整实现
开发语言·c++·算法
海盗猫鸥3 小时前
「C++」string类(2)常用接口
开发语言·c++
yugi9878383 小时前
基于Qt框架开发多功能视频播放器
开发语言·qt
whm27773 小时前
Visual Basic 手工制作工具栏
开发语言·visual studio
小白学大数据5 小时前
增量爬取策略:如何持续监控贝壳网最新成交数据
爬虫·python·性能优化
国服第二切图仔6 小时前
Rust开发实战之操作SQLite数据库——从零构建数据持久化应用
数据库·rust·sqlite