Skip to content
Go back

针对需要认证的 Seafile 文件分享链接编写 Linux 下可用的下载脚本

| 0 Views Edit page

前言

虽然 Seafile 官方提供了文件操作的 API,但是似乎并未提供对需要认证的文件分享链接直接下载的接口。
而我的需求就是这个,没办法只能自己检查每个请求重新封装下流程了。


方案概述

  1. 抓包确定下加密文件分享链接下载的全过程

    • GET 请求文件分享链接获得 sfcsrftokencsrfmiddlewaretokentoken
    • POST 请求文件分享链接获得 sessionid

    请求参数为:csrfmiddlewaretokentokenpassword
    请求头为:Cookie: sfcsrftoken=$sfcsrftoken

    • GET 请求文件分享链接被 302 跳转到实际的文件下载链接

    请求参数为:dl=1
    请求头为:Cookie: sfcsrftoken=$sfcsrftoken;sessionid=$sessionid

    • GET 请求文件下载链接进行实际下载

    请求头为:Cookie: sfcsrftoken=$sfcsrftoken;sessionid=$sessionid

  2. 封装下 Bash 代码

  3. 托管到 GitHub 和 Gitee 使其能被一键调用


操作步骤

一、抓包确定下加密文件分享链接下载的全过程

1、GET 请求文件分享链接获得 sfcsrftokencsrfmiddlewaretokentoken

对应的是打开链接后显示的密码输入页面:
密码输入页面
Request 请求没有需要注意的:

curl 'https://seafile.ceshiku.cn/f/xxxxxxxxxxxxxxxxx8faebc/' --compressed \
  -H 'User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:130.0) Gecko/20100101 Firefox/130.0' \
  -H 'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/png,image/svg+xml,*/*;q=0.8' \
  -H 'Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2' \
  -H 'Accept-Encoding: gzip, deflate, br, zstd' \
  -H 'Connection: keep-alive' \
  -H 'Upgrade-Insecure-Requests: 1' \
  -H 'Sec-Fetch-Dest: document' \
  -H 'Sec-Fetch-Mode: navigate' \
  -H 'Sec-Fetch-Site: none' \
  -H 'Sec-Fetch-User: ?1' \
  -H 'Priority: u=0, i' \

返回的响应头中有 sfcsrftoken

HTTP/1.1 200 OK
Server: nginx/1.26.2
Date: Fri, 20 Sep 2024 07:13:39 GMT
Content-Type: text/html; charset=utf-8
Transfer-Encoding: chunked
Connection: keep-alive
Vary: Cookie, Accept-Language
Content-Language: zh-cn
# 这里为之后的请求 Cookie 设置了 sfcsrftoken 的值
Set-Cookie: sfcsrftoken=XxxxxxxxxxxxxxxxxxxxxxxxxxoOj4oC; expires=Fri, 19 Sep 2025 07:13:39 GMT; Max-Age=31449600; Path=/; SameSite=Lax
Content-Encoding: gzip

返回的响应体中有 csrfmiddlewaretokentoken

<div class="mt-9 mb-4 mx-auto small-panel">
  <p class="intro">如需查看共享的文件/目录,请输入解密密码。</p>
  <form action="/f/xxxxxxxxxxxxxxxxx8faebc/" method="post" id="share-passwd-form">
    <!-- 这里有 csrfmiddlewaretoken 和 token 的值 -->
    <input type="hidden" name="csrfmiddlewaretoken" value="0EmXExxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx">
    <input type="hidden" name="token" value="xxxxxxxxxxxxxxxxx8faebc" />
    <div class="form-group">
      <label for="password">密码</label>
      <input type="password" name="password" id="password" class="form-control" autofocus />
    </div>
    <button type="submit" class="btn btn-primary sf-btn-submit">提交</button>
  </form>
</div>

2、POST 请求文件分享链接获得 sessionid

对应的是输入密码后点击提交的那一步。
Request 请求把 csrfmiddlewaretokentokenpassword 传过去了,同时请求头中设置了 sfcsrftoken

curl "https://seafile.ceshiku.cn/f/xxxxxxxxxxxxxxxxx8faebc/" --compressed \
  -X POST -H "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:130.0) Gecko/20100101 Firefox/130.0" \
  -H "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/png,image/svg+xml,*/*;q=0.8" \
  -H "Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2" \
  -H "Accept-Encoding: gzip, deflate, br, zstd" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -H "Origin: https://seafile.ceshiku.cn" \
  -H "Connection: keep-alive" \
  -H "Referer: https://seafile.ceshiku.cn/f/xxxxxxxxxxxxxxxxx8faebc/" \
  # Cookie 中设置了 sfcsrftoken
  -H "Cookie: sfcsrftoken=XxxxxxxxxxxxxxxxxxxxxxxxxxoOj4oC" \
  -H "Upgrade-Insecure-Requests: 1" \
  -H "Sec-Fetch-Dest: document" \
  -H "Sec-Fetch-Mode: navigate" \
  -H "Sec-Fetch-Site: same-origin" \
  -H "Sec-Fetch-User: ?1" \
  -H "Priority: u=0, i" \
  # POST 的数据中有 csrfmiddlewaretoken、token 和 password
  --data-raw "csrfmiddlewaretoken=0EmXExxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&token=xxxxxxxxxxxxxxxxx8faebc&password=0123456789"

返回的响应体中有 sessionid

HTTP/1.1 200 OK
Server: nginx/1.26.2
Date: Fri, 20 Sep 2024 07:43:03 GMT
Content-Type: text/html; charset=utf-8
Transfer-Encoding: chunked
Connection: keep-alive
Vary: Cookie, Accept-Language
Content-Language: zh-cn
# 这里为之后的请求 Cookie 设置了 sessionid 的值
Set-Cookie: sfcsrftoken=XxxxxxxxxxxxxxxxxxxxxxxxxxoOj4oC; expires=Fri, 19 Sep 2025 07:43:03 GMT; Max-Age=31449600; Path=/; SameSite=Lax
sessionid=urqmxtxxxxxxxxxxxxxxxxxxxxxxxxxibb; expires=Sat, 21 Sep 2024 07:43:03 GMT; HttpOnly; Max-Age=86400; Path=/; SameSite=Lax
Content-Encoding: gzip

3、GET 请求文件分享链接被 302 跳转到实际的文件下载链接

对应的点击下载的那一步。
Request 的请求头中设置了 sfcsrftokensessionid

curl "https://seafile.ceshiku.cn/f/xxxxxxxxxxxxxxxxx8faebc/?dl=1" \
  -H "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:130.0) Gecko/20100101 Firefox/130.0" \
  -H "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/png,image/svg+xml,*/*;q=0.8" \
  -H "Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2" \
  -H "Accept-Encoding: gzip, deflate, br, zstd" \
  -H "Connection: keep-alive" \
  -H "Referer: https://seafile.ceshiku.cn/f/xxxxxxxxxxxxxxxxx8faebc/" \
  # 这里设置了 sfcsrftoken 和 sessionid
  -H "Cookie: sfcsrftoken=XxxxxxxxxxxxxxxxxxxxxxxxxxoOj4oC; sessionid=urqmxtxxxxxxxxxxxxxxxxxxxxxxxxxibb" \
  -H "Upgrade-Insecure-Requests: 1" \
  -H "Sec-Fetch-Dest: document" \
  -H "Sec-Fetch-Mode: navigate" \
  -H "Sec-Fetch-Site: same-origin" \
  -H "Sec-Fetch-User: ?1" \
  -H "Priority: u=0, i"

响应代码是 302,响应头里包含了文件的实际下载地址:

HTTP/1.1 302 Found
Server: nginx/1.26.2
Date: Fri, 20 Sep 2024 07:49:27 GMT
Content-Type: text/html; charset=utf-8
Content-Length: 0
Connection: keep-alive
# 文件的实际下载地址
Location: https://seafile.ceshiku.cn/seafhttp/files/39acfe42-e492-4281-b503-e519f310ac8f/test.txt
Vary: Cookie, Accept-Language
Content-Language: zh-cn
Set-Cookie: sfcsrftoken=XxxxxxxxxxxxxxxxxxxxxxxxxxoOj4oC; expires=Fri, 19 Sep 2025 07:49:27 GMT; Max-Age=31449600; Path=/; SameSite=Lax

4、GET 请求文件下载链接进行实际下载

下载请求的请求头里设置了 sfcsrftokensessionid

curl "https://seafile.ceshiku.cn/seafhttp/files/39acfe42-e492-4281-b503-e519f310ac8f/test.txt" --compressed \
  -H "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:130.0) Gecko/20100101 Firefox/130.0" \
  -H "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/png,image/svg+xml,*/*;q=0.8" \
  -H "Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2" \
  -H "Accept-Encoding: gzip, deflate, br, zstd" \
  -H "Referer: https://seafile.ceshiku.cn/f/xxxxxxxxxxxxxxxxx8faebc/" \
  -H "Connection: keep-alive" \
  # 这里设置了 sfcsrftoken 和 sessionid
  -H "Cookie: sfcsrftoken=XxxxxxxxxxxxxxxxxxxxxxxxxxoOj4oC; sessionid=urqmxtxxxxxxxxxxxxxxxxxxxxxxxxxibb" \
  -H "Upgrade-Insecure-Requests: 1" \
  -H "Sec-Fetch-Dest: document" \
  -H "Sec-Fetch-Mode: navigate" \
  -H "Sec-Fetch-Site: same-origin" \
  -H "Sec-Fetch-User: ?1" \
  -H "Priority: u=0, i"

实际上在这里,直接使用文件下载链接、不设置任何头就可以完成下载:

wget https://seafile.ceshiku.cn/seafhttp/files/39xxxe42-exxx-xxx1-bxxx-exxxfxxxaxxx/test.txt

成功下载
不过显然这并不符合安全规范,也不清楚未来是否会被修复,因此在后续的脚本中依然会带上完整的认证信息头。

二、封装下 Bash 代码

#!/bin/bash

# 1. 检查依赖
# 1.1 检查 curl 是否安装
if ! command -v curl &> /dev/null; then
    echo "curl is not installed. Please install it and try again."
    exit 1
fi
# 1.2 检查 wget 是否安装
if ! command -v wget &> /dev/null; then
    echo "wget is not installed. Please install it and try again."
    exit 1
fi

# 2. 检查参数
url=$1
password=$2
# 未提供 url 或 password
if [ -z "$url" ] || [ -z "$password" ]; then
    echo "Usage: $0 <url> <password>"
    exit 1
fi

# 3. 第一次请求,获得 sfcsrftoken、csrfmiddlewaretoken 和 token
echo "==================== Request 01 start ===================="
# 3.1 使用 curl 请求
response=$(curl -i -s $url)
# 3.2 使用正则 "Set-Cookie: sfcsrftoken=(.*);" 提取 sfcsrftoken
sfcsrftoken=$(echo "$response" | grep -oP 'Set-Cookie: sfcsrftoken=\K[^;]*')
# 3.3 使用正则 '<input type="hidden" name="csrfmiddlewaretoken" value="(.*)"' 提取 csrfmiddlewaretoken
csrfmiddlewaretoken=$(echo "$response" | grep -oP '<input type="hidden" name="csrfmiddlewaretoken" value="\K[^"]*')
# 3.4 使用正则 '<input type="hidden" name="token" value="(.*)"'
token=$(echo "$response" | grep -oP '<input type="hidden" name="token" value="\K[^"]*')
# 3.5 打印值
echo "sfcsrftoken: $sfcsrftoken"
echo "csrfmiddlewaretoken: $csrfmiddlewaretoken"
echo "token: $token"
echo "===================== Request 01 end ====================="
# 3.6 检查是否成功提取
if [ -z "$sfcsrftoken" ] || [ -z "$csrfmiddlewaretoken" ] || [ -z "$token" ]; then
    echo "Failed to extract tokens from the response."
    exit 1
fi

# 4. 发送 POST 请求以获得 sessionid
echo "==================== Request 02 start ===================="
# 4.1 请求体 data-raw 设置为 csrfmiddlewaretoken=$csrfmiddlewaretoken&token=$token&password=$password
data="csrfmiddlewaretoken=$csrfmiddlewaretoken&token=$token&password=$password"
# 请求头的 Cookie 设置为 sfcsrftoken=$sfcsrftoken
header_cookie="sfcsrftoken=$sfcsrftoken"
header_content_type="Content-Type: application/x-www-form-urlencoded"
header_referer="$url"
response=$(curl -i -s -X POST -H "Cookie: $header_cookie" -H "Referer: $header_referer" -d "$data" "$url")
# 4.2 使用正则 'sessionid=(.*);' 提取 sessionid
sessionid=$(echo "$response" | grep -oP 'Set-Cookie: sessionid=\K[^;]*')
# 4.3 打印值
echo "sessionid: $sessionid"
echo "===================== Request 02 end ====================="
# 4.4 检查是否成功提取
if [ -z "$sessionid" ]; then
    echo "Failed to extract sessionid from the response."
    echo "Maybe the password is incorrect. Please check and try again."
    exit 1
fi

# 5. 发送 GET 请求以获取实际的文件地址
echo "==================== Request 03 start ===================="
# 5.1 URL 需要添加参数 dl=1
if [[ "$url" == *"?dl="* ]]; then
    url="${url}&dl=1"
else
    url="${url}?dl=1"
fi
# 请求头的 Cookie 设置为 sfcsrftoken=$sfcsrftoken; sessionid=$sessionid
header_cookie="sfcsrftoken=$sfcsrftoken; sessionid=$sessionid"
header_referer="$url"
response=$(curl -i -s -H "Cookie: $header_cookie" -H "Referer: $header_referer" "$url")
# 5.2 使用正则 "Location: (.*)" 提取文件地址
file_url=$(echo "$response" | grep -oP 'Location: \K.*')
# 5.3 检查是否成功提取
echo "file_url: $file_url"
echo "===================== Request 03 end ====================="
# 5.4 检查是否成功提取
if [ -z "$file_url" ]; then
    echo "Failed to extract file URL from the response."
    exit 1
fi

# 6. 下载文件
echo "===================== Download start ====================="
# 6.1 获取文件名(用 / 分割,取最后一部分,不使用 basename)
filename=$(echo "$file_url" | awk -F/ '{print $NF}')
# 去除末尾的 \r
filename=$(echo "$filename" | tr -d '\r')
if [ -z "$filename" ]; then
    echo "Failed to extract filename from the file URL."
    exit 1
fi
echo "file_url: $file_url"
# 6.2 请求头的 Cookie 设置为 sfcsrftoken=$sfcsrftoken; sessionid=$sessionid
header_cookie="sfcsrftoken=$sfcsrftoken; sessionid=$sessionid"
# 使用 wget 下载文件
wget --header="Cookie: $header_cookie" "$file_url" -O "$filename"
# 6.3 检查下载是否成功
if [ $? -ne 0 ]; then
    echo "Failed to download the file."
    exit 1
fi
echo "====================== Download end ======================"

三、托管到 GitHub 和 Gitee 使其能被一键调用

GitHub 仓库:seafile-scripts
参数可能会变,请总以最新的 GitHub 仓库文档为准

curl -O https://raw.githubusercontent.com/senjianlu/seafile-scripts/master/download.sh && bash download.sh $seafile_share_link $password

境内需要使用 Gitee 的源:

curl -O https://gitee.com/senjianlu/seafile-scripts/raw/master/download.sh && bash download.sh $seafile_share_link $password

Edit page