01.授权码登录open应用

同一个开放应用 id,最多同时有 2 个登录,如果有新的登录,则自动踢掉较早的那一个。

下面的几个案例代码,展示了用 授权码模式 授权登录:

1. 登录 openlist

#!/usr/bin/env python3
# encoding: utf-8

__author__ = "ChenyangGao <https://github.com/ChenyangGao>"
__version__ = (0, 0, 1)

from pathlib import Path
from p115client import P115Client

client = P115Client(Path("~/115-cookies.txt").expanduser())

from base64 import b64decode
from json import loads
from urllib.parse import parse_qsl, unquote, urlsplit

resp = client.request("https://api.oplist.org/115cloud/requests?driver_txt=115cloud_go&server_use=true")
client.cookies = dict(client.cookies)
payload = dict(parse_qsl(urlsplit(unquote(resp["text"][38:])).query))
resp = client.login_authorize_open(payload)
resp = client.request(resp["url"], follow_redirects=False, parse=...)
resp = loads(b64decode(resp.headers["location"][1:]))
client.access_token = resp["access_token"]
client.refresh_token = resp["refresh_token"]

2. 登录 clouddrive

#!/usr/bin/env python3
# encoding: utf-8

__author__ = "ChenyangGao <https://github.com/ChenyangGao>"
__version__ = (0, 0, 1)

from pathlib import Path
from p115client import P115Client

client = P115Client(Path("~/115-cookies.txt").expanduser())

from urllib.parse import parse_qsl, urlsplit

resp = client.login_authorize_open({
    "client_id": 100195313, 
    "redirect_uri": "https://redirect115.zhenyunpan.com", 
    "state": "http://localhost:19798/", 
})
resp = client.request(resp["url"], follow_redirects=False, parse=...)
resp = dict(parse_qsl(urlsplit(resp.headers["location"]).query))
client.access_token = resp["access_token"]
client.refresh_token = resp["refresh_token"]

可以直接借用它的 授权码登录 服务器,打开网页即自动授权,然后立即取得 access_token 和 refresh_token

#!/usr/bin/env python3
# encoding: utf-8

__author__ = "ChenyangGao <https://github.com/ChenyangGao>"
__version__ = (0, 0, 1)

from urllib.parse import urlencode
from fastapi import FastAPI, Request
from fastapi.responses import HTMLResponse, RedirectResponse

app = FastAPI(debug=True)

@app.get("/")
def index(request: Request):
    return HTMLResponse("""\
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>115 开放应用授权示例</title>
    <style>
        body {
            font-family: 'Roboto', sans-serif;
            display: flex;
            justify-content: center;
            align-items: center;
            min-height: 100vh;
            margin: 0;
            background: linear-gradient(135deg, #f0f2f5 0%, #e0e5ec 100%);
            color: #333;
        }

        .container {
            background-color: #ffffff;
            padding: 40px;
            border-radius: 15px;
            box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1);
            text-align: center;
            width: 90%;
            max-width: 600px;
        }

        .fetch-button {
            background-color: #4CAF50;
            color: white;
            padding: 15px 30px;
            border: none;
            border-radius: 8px;
            font-size: 1.1em;
            font-weight: 500;
            cursor: pointer;
            transition: background-color 0.3s ease, transform 0.2s ease;
            box-shadow: 0 4px 10px rgba(76, 175, 80, 0.3);
            margin-bottom: 30px;
        }

        .fetch-button:hover {
            background-color: #45a049;
            transform: translateY(-2px);
        }

        .input-group {
            display: flex;
            align-items: center;
            margin-bottom: 20px;
            background-color: #f9f9f9;
            border: 1px solid #ddd;
            border-radius: 10px;
            padding: 10px;
            box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.05);
        }

        .input-group label {
            min-width: 80px;
            text-align: right;
            margin-right: 15px;
            font-weight: 500;
            color: #555;
        }

        .data-input {
            flex-grow: 1;
            padding: 12px;
            border: 1px solid #ccc;
            border-radius: 8px;
            font-size: 1em;
            color: #333;
            background-color: #fff;
            transition: border-color 0.3s ease;
        }

        .data-input:focus {
            outline: none;
            border-color: #6a82fb;
            box-shadow: 0 0 0 3px rgba(106, 130, 251, 0.2);
        }

        .copy-button {
            background-color: #007bff;
            color: white;
            padding: 10px 15px;
            border: none;
            border-radius: 8px;
            margin-left: 10px;
            cursor: pointer;
            font-size: 0.9em;
            transition: background-color 0.3s ease, transform 0.2s ease;
            box-shadow: 0 2px 8px rgba(0, 123, 255, 0.2);
        }

        .copy-button:hover {
            background-color: #0056b3;
            transform: translateY(-1px);
        }
    </style>
</head>
<body>
    <div class="container">
        <button class="fetch-button" onclick="window.open('/auth', '_self')">点击授权</button>
        <div class="input-group">
            <label for="access_token">access_token</label>
            <input type="text" id="access_token" class="data-input" readonly placeholder="点击按钮获取数据">
            <button class="copy-button" data-target="access_token">复制</button>
        </div>
        <div class="input-group">
            <label for="refresh_token">refresh_token</label>
            <input type="text" id="refresh_token" class="data-input" readonly placeholder="点击按钮获取数据">
            <button class="copy-button" data-target="refresh_token">复制</button>
        </div>
    </div>
    <script>
        document.addEventListener('DOMContentLoaded', () => {
            const params = new URLSearchParams(window.location.search);
            document.getElementById('access_token').value = params.get("access_token") || "";
            document.getElementById('refresh_token').value = params.get("refresh_token") || "";
            document.querySelectorAll('.copy-button').forEach(button => {
                button.addEventListener('click', (event) => {
                    const targetElement = document.getElementById(event.target.dataset.target);
                    targetElement.select();
                    navigator.clipboard.writeText(targetElement.value);
                });
            });
        });
    </script>
</body>
</html>""")

@app.get("/auth")
def auth(request: Request):
    url = "https://passportapi.115.com/open/authorize?" + urlencode({
        "client_id": 100195313, 
        "redirect_uri": "https://redirect115.zhenyunpan.com", 
        "response_type": "code", 
        "state": "{scheme}://{server[0]}:{server[1]}".format_map(request.scope), 
    })
    return RedirectResponse(url, 302)

if __name__ == "__main__":
    from uvicorn import run

    run(app, host="0.0.0.0", port=8115)