【安全性更新】GPT4私人拼车神器——修改works,增加存储share token代码

在帖子【优雅+安全】GPT4私人拼车神器——轻松实现聊天隔离+链接直达+全自动刷新token 基础上,修改worker
代码如下:

addEventListener('fetch', event => {
    event.respondWith(handleRequest(event.request))
})

function parseJwt(token) {
    const base64Url = token.split('.')[1]; // 获取载荷部分
    const base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/'); // 将 Base64Url 转为 Base64
    const jsonPayload = decodeURIComponent(atob(base64).split('').map(function (c) {
        return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2);
    }).join(''));
    return JSON.parse(jsonPayload); // 返回载荷解析后的 JSON 对象
}

function isTokenExpired(token) {
    const payload = parseJwt(token);
    const currentTime = Math.floor(Date.now() / 1000); // 获取当前时间戳(秒)
    return payload.exp < currentTime; // 检查 token 是否过期
}

async function getOAuthLink(shareToken, proxiedDomain) {
    const url = `https://${proxiedDomain}/api/auth/oauth_token`;
    const response = await fetch(url, {
        method: 'POST',
        headers: {
            'Origin': `https://${proxiedDomain}`,
            'Content-Type': 'application/json',
        },
        body: JSON.stringify({
            share_token: shareToken
        })
    })
    const data = await response.json();
    return data.login_url;
}

async function getShareToken(userName, at) {
    const url = 'https://chat.oaifree.com/token/register';
    const body = new URLSearchParams({
        // 此处为获取Share Token时的请求参数,可自行配置
        access_token: at,
        unique_name: userName,
        site_limit: '', // 限制的网站
        expires_in: '0', // token有效期(单位为秒),填 0 则永久有效
        gpt35_limit: '-1', // gpt3.5 对话限制
        gpt4_limit: '-1', // gpt4 对话限制
        show_conversations: 'false', // 是否显示所有人的会话
        show_userinfo: 'false', // 是否显示用户信息
        reset_limit: 'false' // 是否重置对话限制
    }).toString();
    const apiResponse = await fetch(url, {
        method: 'POST',
        headers: {
            'Content-Type': 'application/x-www-form-urlencoded'
        },
        body: body
    });
    const responseText = await apiResponse.text();
    const tokenKeyMatch = /"token_key":"([^"]+)"/.exec(responseText);
    const tokenKey = tokenKeyMatch ? tokenKeyMatch[1] : 'share token 获取失败';
    await oai_global_variables.put(userName,tokenKey);
    return tokenKey;
}

async function handleRequest(request) {
    const url = new URL(request.url);
    const params = new URLSearchParams(url.search);
    const userName = params.get('un');

    // @ts-ignore
    // 验证用户名是否存在
    const users = await oai_global_variables.get("users");
    if (!users.split(",").includes(userName)) {
        return new Response('用户不存在', { status: 404 });
    }

    // @ts-ignore
    const accessToken = await oai_global_variables.get('at');
    if (isTokenExpired(accessToken)) {
        // 给没有refresh token的萌新用(比如我),取消下面这行注释即可享用
         return new Response('当前access token未更新,请联系管理员更新', { status: 401 });

        // 如果 Token 过期,执行获取新 Token 的逻辑
        // const url = 'https://token.oaifree.com/api/auth/refresh';
        // // @ts-ignore
        // const refreshToken = await oai_global_variables.get('rt');  // 实际情况下你可能会从某处获取这个值

        // // 发送 POST 请求
        // const response = await fetch(url, {
        //     method: 'POST',
        //     headers: {
        //         'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8'
        //     },
        //     body: `refresh_token=${refreshToken}`
        // });

        // // 检查响应状态
        // if (response.ok) {
        //     const data = await response.json();  // 假设服务器返回的是 JSON 格式数据
        //     const token = data.access_token; // 直接从 JSON 中获取 access_token

        //     // @ts-ignore
        //     await oai_global_variables.put('at', token);
        // } else {
        //     return new Response('Error fetching access token', { status: response.status });
        // }
    }
    // @ts-ignore
    const tokenPrefix = await oai_global_variables.get('token_prefix');

    let value = await oai_global_variables.get(tokenPrefix + userName);

    if (!value) {
        

        const shareToken = await getShareToken(tokenPrefix + userName, accessToken);
        if (shareToken === 'share token 获取失败') {
            return new Response('token获取失败,请刷新重试', { status: 500 });
        }

        // @ts-ignore
        const proxiedDomain = await oai_global_variables.get('proxied_domain');
        return Response.redirect(await getOAuthLink(shareToken, proxiedDomain), 302);
    }else{
        const proxiedDomain = await oai_global_variables.get('proxied_domain');
        return Response.redirect(await getOAuthLink(value, proxiedDomain), 302);
    }

}
3 个赞

我也想过存储share token,但这样会有个问题,没法自动根据at刷新了,可能需要在rt更新at的时候自动刷新一下所有share token

给大佬顶一顶

这个存储share token,可能会造成功能性问题,不建议加。

顶一顶

那就at失效后,把st也删了,重新请求次。后面改下

1 个赞

最新的work,存储了过期时间、st。

addEventListener('fetch', event => {
    event.respondWith(handleRequest(event.request))
})

function parseJwt(token) {
    const base64Url = token.split('.')[1]; // 获取载荷部分
    const base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/'); // 将 Base64Url 转为 Base64
    const jsonPayload = decodeURIComponent(atob(base64).split('').map(function (c) {
        return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2);
    }).join(''));
   
    return JSON.parse(jsonPayload); // 返回载荷解析后的 JSON 对象
}

function isTokenExpired(token) {

    // 尝试从 KV 存储中获取 token 的过期时间
    let exp = oai_global_variables.get("exp");
  
    // 获取当前时间戳(秒)
    const currentTime = Math.floor(Date.now() / 1000);

    // 如果 KV 中没有该 token 或 token 已过期
    if (!exp || exp < currentTime) {
        // 重新解析 JWT 获取新的 exp
        const payload = parseJwt(token);
        exp = payload.exp;

        // 将新的 exp 存入 KV 存储
        storeExp(payload);

        // 检查 token 是否过期
        return exp < currentTime;
    }

    // 如果 KV 中的 exp 还有效,直接返回检查结果
    return exp < currentTime;
}

async function storeExp(payload) {
    await oai_global_variables.put("exp",payload.exp);
    // 给定的 UNIX 时间戳
    const timestamp = payload.exp;
    
    // 创建一个新的 Date 对象,传入的时间戳需要转换为毫秒
    const date = new Date(timestamp * 1000);
    
    // 格式化日期和时间为 YYYY-MM-DD HH:MM:SS
    const formattedDateTime = date.toISOString().replace('T', ' ').substring(0, 19);
    
    await oai_global_variables.put("exp-data",formattedDateTime);
  }

async function getOAuthLink(shareToken, proxiedDomain) {
    const url = `https://${proxiedDomain}/api/auth/oauth_token`;
    const response = await fetch(url, {
        method: 'POST',
        headers: {
            'Origin': `https://${proxiedDomain}`,
            'Content-Type': 'application/json',
        },
        body: JSON.stringify({
            share_token: shareToken
        })
    })
    const data = await response.json();
    return data.login_url;
}

async function getShareToken(userName, at) {
    const url = 'https://chat.oaifree.com/token/register';
    const body = new URLSearchParams({
        // 此处为获取Share Token时的请求参数,可自行配置
        access_token: at,
        unique_name: userName,
        site_limit: '', // 限制的网站
        expires_in: '0', // token有效期(单位为秒),填 0 则永久有效
        gpt35_limit: '-1', // gpt3.5 对话限制
        gpt4_limit: '-1', // gpt4 对话限制
        show_conversations: 'false', // 是否显示所有人的会话
        show_userinfo: 'false', // 是否显示用户信息
        reset_limit: 'false' // 是否重置对话限制
    }).toString();
    const apiResponse = await fetch(url, {
        method: 'POST',
        headers: {
            'Content-Type': 'application/x-www-form-urlencoded'
        },
        body: body
    });
    const responseText = await apiResponse.text();
    const tokenKeyMatch = /"token_key":"([^"]+)"/.exec(responseText);
    const tokenKey = tokenKeyMatch ? tokenKeyMatch[1] : 'share token 获取失败';
    await oai_global_variables.put(userName,tokenKey);
    return tokenKey;
}

async function deleteKeysWithPrefix(prefix) {
    // 列出所有键
    let keys = await oai_global_variables.list();

    // 筛选出以指定前缀开头的键
    let keysToDelete = keys.keys.filter(key => key.name.startsWith(prefix));

    // 删除这些键
    let deletePromises = keysToDelete.map(key => oai_global_variables.delete(key.name));
    await Promise.all(deletePromises);

    return `Deleted ${keysToDelete.length} keys.`;
}

async function handleRequest(request) {
    const url = new URL(request.url);
    const params = new URLSearchParams(url.search);
    const userName = params.get('un');

    // @ts-ignore
    // 验证用户名是否存在
    const users = await oai_global_variables.get("users");
    if (!users.split(",").includes(userName)) {
        return new Response('用户不存在', { status: 404 });
    }

    // @ts-ignore
    const accessToken = await oai_global_variables.get('at');
    // @ts-ignore
    const tokenPrefix = await oai_global_variables.get('token_prefix');

    if (isTokenExpired(accessToken)) {
        // 给没有refresh token的萌新用(比如我),取消下面这行注释即可享用

        //清空st
        deleteKeysWithPrefix(tokenPrefix);

         return new Response('当前access token未更新,请联系管理员更新', { status: 401 });

        // 如果 Token 过期,执行获取新 Token 的逻辑
        // const url = 'https://token.oaifree.com/api/auth/refresh';
        // // @ts-ignore
        // const refreshToken = await oai_global_variables.get('rt');  // 实际情况下你可能会从某处获取这个值

        // // 发送 POST 请求
        // const response = await fetch(url, {
        //     method: 'POST',
        //     headers: {
        //         'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8'
        //     },
        //     body: `refresh_token=${refreshToken}`
        // });

        // // 检查响应状态
        // if (response.ok) {
        //     const data = await response.json();  // 假设服务器返回的是 JSON 格式数据
        //     const token = data.access_token; // 直接从 JSON 中获取 access_token

        //     // @ts-ignore
        //     await oai_global_variables.put('at', token);
        // } else {
        //     return new Response('Error fetching access token', { status: response.status });
        // }
    }
    

    let value = await oai_global_variables.get(tokenPrefix + userName);

    if (!value) {
        

        const shareToken = await getShareToken(tokenPrefix + userName, accessToken);
        if (shareToken === 'share token 获取失败') {
            return new Response('token获取失败,请刷新重试', { status: 500 });
        }

        // @ts-ignore
        const proxiedDomain = await oai_global_variables.get('proxied_domain');
        return Response.redirect(await getOAuthLink(shareToken, proxiedDomain), 302);
    }else{
        const proxiedDomain = await oai_global_variables.get('proxied_domain');
        return Response.redirect(await getOAuthLink(value, proxiedDomain), 302);
    }

}
1 个赞

人工智能软件开发