mirror of
https://github.com/qaiu/netdisk-fast-download.git
synced 2026-04-30 12:22:57 +00:00
Compare commits
11 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3245a27156 | ||
|
|
ce0fbf65aa | ||
|
|
eb87c3d865 | ||
|
|
990a90e461 | ||
|
|
45cb255a4d | ||
|
|
5e8b8a3575 | ||
|
|
c317e53c24 | ||
|
|
29b59d8450 | ||
|
|
97b7e2f86e | ||
|
|
2f55294b58 | ||
|
|
2161190d9a |
13
README.md
13
README.md
@@ -3,9 +3,11 @@ QQ交流群:1017480890
|
||||
<p align="center">
|
||||
<a href="https://github.com/qaiu/netdisk-fast-download/actions/workflows/maven.yml"><img src="https://img.shields.io/github/actions/workflow/status/qaiu/netdisk-fast-download/maven.yml?branch=v0.1.9b8a&style=flat"></a>
|
||||
<a href="https://www.oracle.com/cn/java/technologies/downloads"><img src="https://img.shields.io/badge/jdk-%3E%3D17-blue"></a>
|
||||
<a href="https://vertx-china.github.io"><img src="https://img.shields.io/badge/vert.x-4.5.22-blue?style=flat"></a>
|
||||
<a href="https://vertx-china.github.io"><img src="https://img.shields.io/badge/vert.x-4.5.24-blue?style=flat"></a>
|
||||
<a href="https://raw.githubusercontent.com/qaiu/netdisk-fast-download/master/LICENSE"><img src="https://img.shields.io/github/license/qaiu/netdisk-fast-download?style=flat"></a>
|
||||
<a href="https://github.com/qaiu/netdisk-fast-download/releases/"><img src="https://img.shields.io/github/v/release/qaiu/netdisk-fast-download?style=flat"></a>
|
||||
<a href="https://atomgit.com/QAIU/netdisk-fast-download"><img src="https://atomgit.com/QAIU/netdisk-fast-download/star/badge.svg" alt="AtomGit"></a>
|
||||
<a href="https://oosmetrics.com/repo/qaiu/netdisk-fast-download"><img src="https://api.oosmetrics.com/api/v1/badge/achievement/826aa27a-6e59-4de5-b7fa-cd189f484035.svg"></a>
|
||||
<p align="center">
|
||||
<a href="https://trendshift.io/repositories/12101" target="_blank"><img src="https://trendshift.io/api/badge/repositories/12101" alt="qaiu%2Fnetdisk-fast-download | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/></a>
|
||||
</p>
|
||||
@@ -23,7 +25,13 @@ QQ交流群:1017480890
|
||||
>
|
||||
</div>
|
||||
|
||||
> netdisk-fast-download网盘直链解析可以把云盘分享链接转为直链,可广泛应用于各类下载站,资源站,个人博客,图床,APP下载更新,视频点播等领域。支持市面各大主流云盘的文件分享以及文件夹分享链接,已支持蓝奏云/蓝奏云优享/奶牛快传/移动云云空间/小飞机盘/亿方云/123云盘/Cloudreve等,支持加密分享,以及部分网盘文件夹分享。
|
||||
## 国内镜像
|
||||
|
||||
本项目同步托管于 **AtomGit**,国内访问更流畅:👉 [https://atomgit.com/QAIU/netdisk-fast-download](https://atomgit.com/QAIU/netdisk-fast-download)
|
||||
|
||||
|
||||
## 介绍
|
||||
> netdisk-fast-download网盘直链解析可以把云盘分享链接转为直链,可广泛应用于各类下载站,资源站,个人博客,图床,APP下载更新,视频点播等领域。支持市面各大主流云盘的文件分享以及文件夹分享链接,已支持蓝奏云/蓝奏云优享/移动云云空间/小飞机盘/亿方云/123云盘/Cloudreve等,支持加密分享,以及部分网盘文件夹分享。
|
||||
|
||||
[官方文档](https://nfd-parser.github.io/)
|
||||
[API接入](https://nfdparser.apifox.cn/)
|
||||
@@ -62,7 +70,6 @@ https://nfd-parser.github.io/nfd-preview/preview.html?src=https%3A%2F%2Flz.qaiu.
|
||||
**注意⚠️请不要过度依赖 lz.qaiu.top,建议本地搭建或者云服务器自行搭建。请求量过多的话服务器可能会被云盘厂商限制,遇到解析失败的分享链接不要着急提issues,请先检查分享是否有效。**
|
||||
|
||||
## 网盘支持情况:
|
||||
> 20230905 奶牛云直链做了防盗链,需加入请求头:Referer: https://cowtransfer.com/
|
||||
> 20230824 123云盘解析大文件(>100MB)失效,需要登录
|
||||
> 20230722 UC网盘解析失效,需要登录
|
||||
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
app.version=${project.version}
|
||||
build=${build.timestamp}
|
||||
build=${maven.build.timestamp}
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
## 测试配置
|
||||
|
||||
### 小飞机网盘 ✅
|
||||
- **用户名**: 15764091073
|
||||
- **用户名**: 15x
|
||||
- **URL**: https://share.feijipan.com/s/ZWYoZ31c
|
||||
- **文件**: 资源.rar (1.13 GB)
|
||||
- **认证方式**: username/password
|
||||
@@ -32,7 +32,7 @@
|
||||
```
|
||||
=== 测试小飞机网盘解析(带认证)===
|
||||
分享链接: https://share.feijipan.com/s/ZWYoZ31c
|
||||
用户名: 15764091073
|
||||
用户名: 15x
|
||||
密码: ******
|
||||
|
||||
开始解析...
|
||||
|
||||
BIN
web-front/img/image.png
Normal file
BIN
web-front/img/image.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.5 MiB |
Binary file not shown.
|
Before Width: | Height: | Size: 69 KiB |
BIN
web-front/public/images/logo01.png
Normal file
BIN
web-front/public/images/logo01.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 302 KiB |
BIN
web-front/public/images/logo02.png
Normal file
BIN
web-front/public/images/logo02.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 482 KiB |
@@ -5,6 +5,15 @@ const axiosInstance = axios.create({
|
||||
withCredentials: true // 重要:允许跨域请求携带cookie
|
||||
});
|
||||
|
||||
// 请求拦截器:将存储的Token添加到Authorization请求头
|
||||
axiosInstance.interceptors.request.use(config => {
|
||||
const token = localStorage.getItem('playground_token');
|
||||
if (token) {
|
||||
config.headers['Authorization'] = `Bearer ${token}`;
|
||||
}
|
||||
return config;
|
||||
});
|
||||
|
||||
/**
|
||||
* 演练场API服务
|
||||
*/
|
||||
@@ -30,7 +39,12 @@ export const playgroundApi = {
|
||||
async login(password) {
|
||||
try {
|
||||
const response = await axiosInstance.post('/v2/playground/login', { password });
|
||||
return response.data;
|
||||
const data = response.data;
|
||||
// 登录成功时从响应中提取并保存Token
|
||||
if ((data.code === 200 || data.success) && data.data?.token) {
|
||||
localStorage.setItem('playground_token', data.data.token);
|
||||
}
|
||||
return data;
|
||||
} catch (error) {
|
||||
throw new Error(error.response?.data?.error || error.message || '登录失败');
|
||||
}
|
||||
|
||||
@@ -68,7 +68,7 @@
|
||||
</div>
|
||||
<div class="demo-basic--circle">
|
||||
<div class="block" style="text-align: center;">
|
||||
<img :height="150" src="../../public/images/lanzou111.png" alt="lz">
|
||||
<img :height="150" src="../../public/images/logo01.png" alt="lz">
|
||||
</div>
|
||||
</div>
|
||||
<!-- 项目简介移到卡片内 -->
|
||||
|
||||
@@ -1146,10 +1146,11 @@ function parseById(shareLinkInfo, http, logger) {
|
||||
const isAuthed = res.data.authed || res.data.public;
|
||||
authed.value = isAuthed;
|
||||
|
||||
// 如果后端session已失效,清除localStorage
|
||||
// 如果后端认证已失效,清除localStorage中的认证信息
|
||||
if (!isAuthed && savedAuth === 'true') {
|
||||
localStorage.removeItem('playground_authed');
|
||||
localStorage.removeItem('playground_auth_time');
|
||||
localStorage.removeItem('playground_token');
|
||||
}
|
||||
|
||||
return isAuthed;
|
||||
|
||||
@@ -4,6 +4,10 @@ import io.vertx.core.json.JsonObject;
|
||||
import lombok.Data;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.security.SecureRandom;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* JS演练场配置
|
||||
*
|
||||
@@ -13,11 +17,21 @@ import lombok.extern.slf4j.Slf4j;
|
||||
@Slf4j
|
||||
public class PlaygroundConfig {
|
||||
|
||||
/** Token有效期:24小时 */
|
||||
private static final long TOKEN_EXPIRY_MS = 24 * 60 * 60 * 1000L;
|
||||
|
||||
private static final SecureRandom SECURE_RANDOM = new SecureRandom();
|
||||
|
||||
/**
|
||||
* 单例实例
|
||||
*/
|
||||
private static PlaygroundConfig instance;
|
||||
|
||||
/**
|
||||
* 已颁发的认证Token及其创建时间
|
||||
*/
|
||||
private final Map<String, Long> validTokens = new ConcurrentHashMap<>();
|
||||
|
||||
/**
|
||||
* 是否启用演练场
|
||||
* 默认false,不启用
|
||||
@@ -42,6 +56,39 @@ public class PlaygroundConfig {
|
||||
private PlaygroundConfig() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成并存储一个新的认证Token,同时清理过期Token
|
||||
*/
|
||||
public String generateToken() {
|
||||
// 清理过期Token,防止Map无限增长
|
||||
long now = System.currentTimeMillis();
|
||||
validTokens.entrySet().removeIf(e -> now - e.getValue() > TOKEN_EXPIRY_MS);
|
||||
// 使用SecureRandom生成32字节的密码学安全Token
|
||||
byte[] bytes = new byte[32];
|
||||
SECURE_RANDOM.nextBytes(bytes);
|
||||
StringBuilder sb = new StringBuilder(64);
|
||||
for (byte b : bytes) {
|
||||
sb.append(String.format("%02x", b));
|
||||
}
|
||||
String token = sb.toString();
|
||||
validTokens.put(token, now);
|
||||
return token;
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验Token是否合法且未过期
|
||||
*/
|
||||
public boolean validateToken(String token) {
|
||||
if (token == null || token.isEmpty()) return false;
|
||||
Long createdAt = validTokens.get(token);
|
||||
if (createdAt == null) return false;
|
||||
if (System.currentTimeMillis() - createdAt > TOKEN_EXPIRY_MS) {
|
||||
validTokens.remove(token);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取单例实例
|
||||
*/
|
||||
|
||||
@@ -21,7 +21,6 @@ import io.vertx.core.http.HttpServerRequest;
|
||||
import io.vertx.core.http.HttpServerResponse;
|
||||
import io.vertx.core.json.JsonObject;
|
||||
import io.vertx.ext.web.RoutingContext;
|
||||
import io.vertx.ext.web.Session;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
@@ -47,7 +46,6 @@ public class PlaygroundApi {
|
||||
|
||||
private static final int MAX_PARSER_COUNT = 100;
|
||||
private static final int MAX_CODE_LENGTH = 128 * 1024; // 128KB 代码长度限制
|
||||
private static final String SESSION_AUTH_KEY = "playgroundAuthed";
|
||||
private final DbService dbService = AsyncServiceUtil.getAsyncServiceInstance(DbService.class);
|
||||
|
||||
/**
|
||||
@@ -74,14 +72,14 @@ public class PlaygroundApi {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 否则检查Session中的认证状态
|
||||
Session session = ctx.session();
|
||||
if (session == null) {
|
||||
return false;
|
||||
// 检查 Authorization: Bearer <token> 请求头
|
||||
String authHeader = ctx.request().getHeader("Authorization");
|
||||
if (authHeader != null && authHeader.startsWith("Bearer ") && authHeader.length() > 7) {
|
||||
String token = authHeader.substring(7).trim();
|
||||
return config.validateToken(token);
|
||||
}
|
||||
|
||||
Boolean authed = session.get(SESSION_AUTH_KEY);
|
||||
return authed != null && authed;
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -116,12 +114,8 @@ public class PlaygroundApi {
|
||||
try {
|
||||
PlaygroundConfig config = PlaygroundConfig.getInstance();
|
||||
|
||||
// 如果是公开模式,直接成功
|
||||
// 如果是公开模式,直接成功(不需要token)
|
||||
if (config.isPublic()) {
|
||||
Session session = ctx.session();
|
||||
if (session != null) {
|
||||
session.put(SESSION_AUTH_KEY, true);
|
||||
}
|
||||
promise.complete(JsonResult.success("公开模式,无需密码").toJsonObject());
|
||||
return promise.future();
|
||||
}
|
||||
@@ -137,11 +131,9 @@ public class PlaygroundApi {
|
||||
|
||||
// 验证密码
|
||||
if (config.getPassword().equals(password)) {
|
||||
Session session = ctx.session();
|
||||
if (session != null) {
|
||||
session.put(SESSION_AUTH_KEY, true);
|
||||
}
|
||||
promise.complete(JsonResult.success("登录成功").toJsonObject());
|
||||
String token = config.generateToken();
|
||||
JsonObject tokenData = new JsonObject().put("token", token);
|
||||
promise.complete(JsonResult.data(tokenData).toJsonObject());
|
||||
} else {
|
||||
promise.complete(JsonResult.error("密码错误").toJsonObject());
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# 要激活的配置: app-配置名称.yml
|
||||
active: local
|
||||
active: dev
|
||||
# 控制台输出的版权文字
|
||||
copyright: QAIU
|
||||
|
||||
Reference in New Issue
Block a user