mirror of
https://github.com/qaiu/netdisk-fast-download.git
synced 2025-12-16 20:33:03 +00:00
- 加入小飞机盘直链解析
- 优化代码
This commit is contained in:
25
README.md
25
README.md
@@ -13,8 +13,12 @@
|
|||||||
- [ ] 登录, 上传, 下载, 分享
|
- [ ] 登录, 上传, 下载, 分享
|
||||||
- [x] 直链解析
|
- [x] 直链解析
|
||||||
- UC网盘 (uc)
|
- UC网盘 (uc)
|
||||||
- [ ] 登录, 上传, 下载, 分享
|
- [ ] 登录, 上传, 下载, 分享
|
||||||
- [x] 直链解析
|
- [x] 直链解析
|
||||||
|
- 小飞机网盘 (fj)
|
||||||
|
- [ ] 登录, 上传, 下载, 分享
|
||||||
|
- [x] 直链解析
|
||||||
|
- 文叔叔 (ws)
|
||||||
- 夸克网盘 (qk)
|
- 夸克网盘 (qk)
|
||||||
- TODO
|
- TODO
|
||||||
|
|
||||||
@@ -30,7 +34,7 @@ API接口
|
|||||||
http(s)://you_host/网盘标识/分享id(#分享密码)
|
http(s)://you_host/网盘标识/分享id(#分享密码)
|
||||||
2. 获取解析后的直链--JSON格式
|
2. 获取解析后的直链--JSON格式
|
||||||
http(s)://you_host/json/网盘标识/分享id(#分享密码)
|
http(s)://you_host/json/网盘标识/分享id(#分享密码)
|
||||||
|
3. 有些网盘的加密分享的密码可以忽略: 如移动云空间,小飞机网盘
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
@@ -63,11 +67,12 @@ TODO:
|
|||||||
|
|
||||||
# 网盘对比
|
# 网盘对比
|
||||||
|
|
||||||
| 网盘名称 | 可直接下载分享 | 加密分享 | 初始网盘空间 | 单文件大小限制 | 登录接口 |
|
| 网盘名称 | 可直接下载分享 | 加密分享 | 初始网盘空间 | 单文件大小限制 | 登录接口 |
|
||||||
|-------|-------------|----------|----------|----------------|------|
|
|------------|---------|----------|----------|---------|------|
|
||||||
| 蓝奏云 | √ | √ | 不限空间 | 100M | TODO |
|
| 蓝奏云 | √ | √ | 不限空间 | 100M | TODO |
|
||||||
| 奶牛快传 | √ | X | 10G | 不限大小 | TODO |
|
| 奶牛快传 | √ | X | 10G | 不限大小 | TODO |
|
||||||
| 移动云空间 | √ | √(密码可忽略) | 5G(个人) | 不限大小 | TODO |
|
| 移动云空间 | √ | √(密码可忽略) | 5G(个人) | 不限大小 | TODO |
|
||||||
| UC网盘 | √ | √ | 10G | 不限大小 | TODO |
|
| UC网盘 | √ | √ | 10G | 不限大小 | TODO |
|
||||||
| 夸克网盘 | √(>10M需要登录) | √ | 10G(20G) | 不限大小(>10M需要登录) | X |
|
| 小飞机网盘 | √ | √(密码可忽略) | 10G | 不限大小 | TODO |
|
||||||
|
| 夸克网盘(TODO) | 需要登录 | √ | 10G(20G) | 不限大小 | TODO |
|
||||||
|
|
||||||
|
|||||||
239
web-service/src/main/java/cn/qaiu/lz/common/util/AESUtils.java
Normal file
239
web-service/src/main/java/cn/qaiu/lz/common/util/AESUtils.java
Normal file
@@ -0,0 +1,239 @@
|
|||||||
|
package cn.qaiu.lz.common.util;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
|
import javax.crypto.*;
|
||||||
|
import javax.crypto.spec.SecretKeySpec;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.security.InvalidKeyException;
|
||||||
|
import java.security.Key;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import java.util.Base64;
|
||||||
|
import java.util.HexFormat;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* AES加解密工具类
|
||||||
|
*
|
||||||
|
* @author qaiu
|
||||||
|
**/
|
||||||
|
public class AESUtils {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* AES密钥标识
|
||||||
|
*/
|
||||||
|
public static final String SIGN_AES = "AES";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 密码器AES模式
|
||||||
|
*/
|
||||||
|
public static final String CIPHER_AES = "AES/ECB/PKCS5Padding";
|
||||||
|
|
||||||
|
public static final String CIPHER_AES2 = "YbQHZqK/PdQql2+7ATcPQHREAxt0Hn0Ob9v317QirZM=";
|
||||||
|
|
||||||
|
public static final String CIPHER_AES0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 秘钥长度
|
||||||
|
*/
|
||||||
|
public static final int KEY_LENGTH = 16;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 密钥长度128
|
||||||
|
*/
|
||||||
|
public static final int KEY_SIZE_128_LENGTH = 128;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 密钥长度192
|
||||||
|
*/
|
||||||
|
public static final int KEY_SIZE_192_LENGTH = 192;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 密钥长度256
|
||||||
|
*/
|
||||||
|
public static final int KEY_SIZE_256_LENGTH = 256;
|
||||||
|
|
||||||
|
static {
|
||||||
|
try {
|
||||||
|
CIPHER_AES0 = decryptByBase64AES(CIPHER_AES2, CIPHER_AES);
|
||||||
|
} catch (IllegalBlockSizeException | BadPaddingException | NoSuchPaddingException | NoSuchAlgorithmException |
|
||||||
|
InvalidKeyException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 随机生成密钥,请使用合适的长度128 192 256
|
||||||
|
*/
|
||||||
|
public static Key createKeyString(int keySize) throws NoSuchAlgorithmException {
|
||||||
|
KeyGenerator keyGenerator = KeyGenerator.getInstance(SIGN_AES);
|
||||||
|
keyGenerator.init(keySize);
|
||||||
|
SecretKey secretKey = keyGenerator.generateKey();
|
||||||
|
return new SecretKeySpec(secretKey.getEncoded(), SIGN_AES);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成Key对象
|
||||||
|
*/
|
||||||
|
public static Key generateKey(String keyString) {
|
||||||
|
if (keyString.length() > KEY_LENGTH) {
|
||||||
|
keyString = keyString.substring(0, KEY_LENGTH);
|
||||||
|
} else if (keyString.length() < KEY_LENGTH) {
|
||||||
|
keyString = StringUtils.rightPad(keyString, 16, 'L');
|
||||||
|
}
|
||||||
|
return new SecretKeySpec(keyString.getBytes(), SIGN_AES);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* AES加密
|
||||||
|
*
|
||||||
|
* @param source 原文
|
||||||
|
* @param keyString 秘钥
|
||||||
|
* @return byte arrays
|
||||||
|
*/
|
||||||
|
public static byte[] encryptByAES(String source, String keyString) throws NoSuchPaddingException,
|
||||||
|
NoSuchAlgorithmException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException {
|
||||||
|
Cipher cipher = Cipher.getInstance(CIPHER_AES);
|
||||||
|
cipher.init(Cipher.ENCRYPT_MODE, generateKey(keyString));
|
||||||
|
return cipher.doFinal(source.getBytes(StandardCharsets.UTF_8));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] encryptByAES(String source, Key key) throws NoSuchPaddingException,
|
||||||
|
NoSuchAlgorithmException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException {
|
||||||
|
Cipher cipher = Cipher.getInstance(CIPHER_AES);
|
||||||
|
cipher.init(Cipher.ENCRYPT_MODE, key);
|
||||||
|
return cipher.doFinal(source.getBytes(StandardCharsets.UTF_8));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* AES加密Base64
|
||||||
|
*
|
||||||
|
* @param source 原文
|
||||||
|
* @param keyString 秘钥
|
||||||
|
* @return BASE64
|
||||||
|
*/
|
||||||
|
public static String encryptBase64ByAES(String source, String keyString) throws NoSuchPaddingException,
|
||||||
|
NoSuchAlgorithmException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException {
|
||||||
|
byte[] encrypted = encryptByAES(source, keyString);
|
||||||
|
return Base64.getEncoder().encodeToString(encrypted);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String encryptBase64ByAES(String source, Key key) throws NoSuchPaddingException,
|
||||||
|
NoSuchAlgorithmException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException {
|
||||||
|
byte[] encrypted = encryptByAES(source, key);
|
||||||
|
return Base64.getEncoder().encodeToString(encrypted);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* AES加密Hex
|
||||||
|
*
|
||||||
|
* @param source 原文
|
||||||
|
* @param keyString 秘钥
|
||||||
|
*/
|
||||||
|
public static String encryptHexByAES(String source, String keyString) throws NoSuchPaddingException,
|
||||||
|
NoSuchAlgorithmException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException {
|
||||||
|
byte[] encrypted = encryptByAES(source, keyString);
|
||||||
|
return HexFormat.of().formatHex(encrypted);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String encryptHexByAES(String source, Key key) throws NoSuchPaddingException,
|
||||||
|
NoSuchAlgorithmException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException {
|
||||||
|
byte[] encrypted = encryptByAES(source, key);
|
||||||
|
return HexFormat.of().formatHex(encrypted);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String encrypt2Hex(String source) {
|
||||||
|
try {
|
||||||
|
return encryptHexByAES(source, CIPHER_AES0);
|
||||||
|
} catch (NoSuchPaddingException | NoSuchAlgorithmException | InvalidKeyException | IllegalBlockSizeException |
|
||||||
|
BadPaddingException e) {
|
||||||
|
throw new RuntimeException("加密失败: "+ e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* AES解密
|
||||||
|
*
|
||||||
|
* @param encrypted 密文 byte
|
||||||
|
* @param keyString 秘钥
|
||||||
|
*/
|
||||||
|
public static String decryptByAES(byte[] encrypted, String keyString) throws IllegalBlockSizeException,
|
||||||
|
BadPaddingException, InvalidKeyException, NoSuchPaddingException, NoSuchAlgorithmException {
|
||||||
|
return decryptByAES(encrypted, generateKey(keyString));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String decryptByAES(byte[] encrypted, Key key) throws IllegalBlockSizeException,
|
||||||
|
BadPaddingException, InvalidKeyException, NoSuchPaddingException, NoSuchAlgorithmException {
|
||||||
|
Cipher cipher = Cipher.getInstance(CIPHER_AES);
|
||||||
|
cipher.init(Cipher.DECRYPT_MODE, key);
|
||||||
|
byte[] decrypted = cipher.doFinal(encrypted);
|
||||||
|
return new String(decrypted, StandardCharsets.UTF_8);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* AES解密
|
||||||
|
*
|
||||||
|
* @param encrypted 密文 Hex
|
||||||
|
* @param keyString 秘钥
|
||||||
|
*/
|
||||||
|
public static String decryptByHexAES(String encrypted, String keyString) throws IllegalBlockSizeException,
|
||||||
|
BadPaddingException, InvalidKeyException, NoSuchPaddingException, NoSuchAlgorithmException {
|
||||||
|
return decryptByAES(HexFormat.of().parseHex(encrypted), keyString);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String decryptByHexAES(String encrypted, Key key) throws IllegalBlockSizeException,
|
||||||
|
BadPaddingException, InvalidKeyException, NoSuchPaddingException, NoSuchAlgorithmException {
|
||||||
|
return decryptByAES(HexFormat.of().parseHex(encrypted), key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* AES解密
|
||||||
|
*
|
||||||
|
* @param encrypted 密文 Base64
|
||||||
|
* @param keyString 秘钥
|
||||||
|
*/
|
||||||
|
public static String decryptByBase64AES(String encrypted, String keyString) throws IllegalBlockSizeException,
|
||||||
|
BadPaddingException, InvalidKeyException, NoSuchPaddingException, NoSuchAlgorithmException {
|
||||||
|
return decryptByAES(Base64.getDecoder().decode(encrypted), keyString);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String decryptByBase64AES(String encrypted, Key key) throws IllegalBlockSizeException,
|
||||||
|
BadPaddingException, InvalidKeyException, NoSuchPaddingException, NoSuchAlgorithmException {
|
||||||
|
return decryptByAES(Base64.getDecoder().decode(encrypted), key);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ================================飞机盘Id解密========================================== //
|
||||||
|
private static final char[] array = {
|
||||||
|
'T', 'U', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
|
||||||
|
'0', 'M', 'N', 'O', 'P', 'X', 'Y', 'Z', 'V', 'W',
|
||||||
|
'Q', '1', '2', '3', '4', 'a', 'b', 'c', 'd', 'e',
|
||||||
|
'5', '6', '7', '8', '9', 'v', 'w', 'x', 'y', 'z',
|
||||||
|
'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
|
||||||
|
'p', 'q', 'r', 's', 't', 'u', 'L', 'R', 'S', 'I',
|
||||||
|
'J', 'K'};
|
||||||
|
|
||||||
|
private static int decodeChar(char c) {
|
||||||
|
for (int i = 0; i < array.length; i++) {
|
||||||
|
if (c == array[i]) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// id解密
|
||||||
|
public static int idEncrypt(String str) {
|
||||||
|
// 倍数
|
||||||
|
int multiple = 1;
|
||||||
|
int result = 0;
|
||||||
|
if (StringUtils.isNotEmpty(str) && str.length() > 4) {
|
||||||
|
str = str.substring(2, str.length() - 2);
|
||||||
|
char c;
|
||||||
|
for (int i = 0; i < str.length(); i++) {
|
||||||
|
c = str.charAt(str.length() - i - 1);
|
||||||
|
result += decodeChar(c) * multiple;
|
||||||
|
multiple = multiple * 62;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
package cn.qaiu.lz.common.util;
|
||||||
|
|
||||||
|
public class CommonUtils {
|
||||||
|
|
||||||
|
public static String parseURL(String urlPrefix, String url) {
|
||||||
|
if (!url.startsWith(urlPrefix)) {
|
||||||
|
url = urlPrefix + url;
|
||||||
|
}
|
||||||
|
return url.substring(urlPrefix.length());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
package cn.qaiu.lz.common.util;
|
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取连接
|
|
||||||
*
|
|
||||||
* @author <a href="https://qaiu.top">QAIU</a>
|
|
||||||
*/
|
|
||||||
public enum ConnectUtil {
|
|
||||||
|
|
||||||
// 实现枚举单例
|
|
||||||
INSTANCE;
|
|
||||||
|
|
||||||
private static final Logger LOGGER = LoggerFactory.getLogger(ConnectUtil.class);
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -14,7 +14,7 @@ import lombok.extern.slf4j.Slf4j;
|
|||||||
*/
|
*/
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class EcTool {
|
public class EcTool {
|
||||||
private static final String FULL_URL_PREFIX = "https://www.ecpan.cn/drive/fileextoverrid" +
|
private static final String SHARE_URL_PREFIX = "https://www.ecpan.cn/drive/fileextoverrid" +
|
||||||
".do?chainUrlTemplate=https:%2F%2Fwww.ecpan" +
|
".do?chainUrlTemplate=https:%2F%2Fwww.ecpan" +
|
||||||
".cn%2Fweb%2F%23%2FyunpanProxy%3Fpath%3D%252F%2523%252Fdrive%252Foutside&parentId=-1&data={dataKey}";
|
".cn%2Fweb%2F%23%2FyunpanProxy%3Fpath%3D%252F%2523%252Fdrive%252Foutside&parentId=-1&data={dataKey}";
|
||||||
|
|
||||||
@@ -26,7 +26,7 @@ public class EcTool {
|
|||||||
Promise<String> promise = Promise.promise();
|
Promise<String> promise = Promise.promise();
|
||||||
WebClient client = WebClient.create(VertxHolder.getVertxInstance());
|
WebClient client = WebClient.create(VertxHolder.getVertxInstance());
|
||||||
// 第一次请求 获取文件信息
|
// 第一次请求 获取文件信息
|
||||||
client.getAbs(UriTemplate.of(FULL_URL_PREFIX)).setTemplateParam("dataKey", dataKey).send().onSuccess(res -> {
|
client.getAbs(UriTemplate.of(SHARE_URL_PREFIX)).setTemplateParam("dataKey", dataKey).send().onSuccess(res -> {
|
||||||
JsonObject jsonObject = res.bodyAsJsonObject();
|
JsonObject jsonObject = res.bodyAsJsonObject();
|
||||||
log.debug("ecPan get file info -> {}", jsonObject);
|
log.debug("ecPan get file info -> {}", jsonObject);
|
||||||
JsonObject fileInfo = jsonObject
|
JsonObject fileInfo = jsonObject
|
||||||
|
|||||||
75
web-service/src/main/java/cn/qaiu/lz/common/util/FjTool.java
Normal file
75
web-service/src/main/java/cn/qaiu/lz/common/util/FjTool.java
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
package cn.qaiu.lz.common.util;
|
||||||
|
|
||||||
|
import cn.qaiu.vx.core.util.VertxHolder;
|
||||||
|
import io.vertx.core.Future;
|
||||||
|
import io.vertx.core.MultiMap;
|
||||||
|
import io.vertx.core.Promise;
|
||||||
|
import io.vertx.core.json.JsonObject;
|
||||||
|
import io.vertx.ext.web.client.WebClient;
|
||||||
|
import io.vertx.ext.web.client.WebClientOptions;
|
||||||
|
import io.vertx.uritemplate.UriTemplate;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 小飞机网盘
|
||||||
|
*
|
||||||
|
* @version V016_230609
|
||||||
|
*/
|
||||||
|
public class FjTool {
|
||||||
|
|
||||||
|
public static final String SHARE_URL_PREFIX = "https://www.feijix.com/s/";
|
||||||
|
private static final String API_URL_PREFIX = "https://api.feijipan.com/ws/";
|
||||||
|
|
||||||
|
private static final String FIRST_REQUEST_URL = API_URL_PREFIX + "recommend/list?devType=6&devModel=Chrome&extra" +
|
||||||
|
"=2&shareId={shareId}&type=0&offset=1&limit=60";
|
||||||
|
|
||||||
|
private static final String SECOND_REQUEST_URL = API_URL_PREFIX + "file/redirect?downloadId={fidEncode}&enable=1" +
|
||||||
|
"&devType=6&uuid={uuid}×tamp={ts}&auth={auth}";
|
||||||
|
|
||||||
|
public static Future<String> parse(String data) {
|
||||||
|
String dataKey = CommonUtils.parseURL(SHARE_URL_PREFIX, data);
|
||||||
|
|
||||||
|
Promise<String> promise = Promise.promise();
|
||||||
|
WebClient client = WebClient.create(VertxHolder.getVertxInstance(),
|
||||||
|
new WebClientOptions().setFollowRedirects(false));
|
||||||
|
String shareId = String.valueOf(AESUtils.idEncrypt(dataKey));
|
||||||
|
|
||||||
|
// 第一次请求 获取文件信息
|
||||||
|
// POST https://api.feijipan.com/ws/recommend/list?devType=6&devModel=Chrome&extra=2&shareId=146731&type=0&offset=1&limit=60
|
||||||
|
client.postAbs(UriTemplate.of(FIRST_REQUEST_URL)).setTemplateParam("shareId", shareId).send().onSuccess(res -> {
|
||||||
|
JsonObject resJson = res.bodyAsJsonObject();
|
||||||
|
if (resJson.getInteger("code") != 200) {
|
||||||
|
promise.fail(FIRST_REQUEST_URL + " 返回异常: " + resJson);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (resJson.getJsonArray("list").size() == 0) {
|
||||||
|
promise.fail(FIRST_REQUEST_URL + " 解析文件列表为空: " + resJson);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 文件Id
|
||||||
|
String fileId = resJson.getJsonArray("list").getJsonObject(0).getString("fileIds");
|
||||||
|
// 其他参数
|
||||||
|
long nowTs = System.currentTimeMillis();
|
||||||
|
String tsEncode = AESUtils.encrypt2Hex(Long.toString(nowTs));
|
||||||
|
String uuid = UUID.randomUUID().toString();
|
||||||
|
String fidEncode = AESUtils.encrypt2Hex(fileId + "|");
|
||||||
|
String auth = AESUtils.encrypt2Hex(fileId + "|" + nowTs);
|
||||||
|
// 第二次请求
|
||||||
|
client.getAbs(UriTemplate.of(SECOND_REQUEST_URL))
|
||||||
|
.setTemplateParam("fidEncode", fidEncode)
|
||||||
|
.setTemplateParam("uuid", uuid)
|
||||||
|
.setTemplateParam("ts", tsEncode)
|
||||||
|
.setTemplateParam("auth", auth).send().onSuccess(res2 -> {
|
||||||
|
MultiMap headers = res2.headers();
|
||||||
|
if (!headers.contains("Location")) {
|
||||||
|
promise.fail(SECOND_REQUEST_URL + " 未找到重定向URL: \n" + res.headers());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
promise.complete(headers.get("Location"));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return promise.future();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -16,7 +16,7 @@ import lombok.extern.slf4j.Slf4j;
|
|||||||
public class UcTool {
|
public class UcTool {
|
||||||
private static final String API_URL_PREFIX = "https://pc-api.uc.cn/1/clouddrive/";
|
private static final String API_URL_PREFIX = "https://pc-api.uc.cn/1/clouddrive/";
|
||||||
|
|
||||||
public static final String FULL_URL_PREFIX = "https://fast.uc.cn/s/";
|
public static final String SHARE_URL_PREFIX = "https://fast.uc.cn/s/";
|
||||||
|
|
||||||
private static final String FIRST_REQUEST_URL = API_URL_PREFIX + "share/sharepage/token?entry=ft&fr=pc&pr" +
|
private static final String FIRST_REQUEST_URL = API_URL_PREFIX + "share/sharepage/token?entry=ft&fr=pc&pr" +
|
||||||
"=UCBrowser";
|
"=UCBrowser";
|
||||||
@@ -27,11 +27,8 @@ public class UcTool {
|
|||||||
private static final String THIRD_REQUEST_URL = API_URL_PREFIX + "file/download?entry=ft&fr=pc&pr=UCBrowser";
|
private static final String THIRD_REQUEST_URL = API_URL_PREFIX + "file/download?entry=ft&fr=pc&pr=UCBrowser";
|
||||||
|
|
||||||
public static Future<String> parse(String data, String code) {
|
public static Future<String> parse(String data, String code) {
|
||||||
if (!data.startsWith(FULL_URL_PREFIX)) {
|
var dataKey = CommonUtils.parseURL(SHARE_URL_PREFIX, data);
|
||||||
data = FULL_URL_PREFIX + data;
|
|
||||||
}
|
|
||||||
var passcode = (code == null) ? "" : code;
|
var passcode = (code == null) ? "" : code;
|
||||||
var dataKey = data.substring(FULL_URL_PREFIX.length());
|
|
||||||
Promise<String> promise = Promise.promise();
|
Promise<String> promise = Promise.promise();
|
||||||
var client = WebClient.create(VertxHolder.getVertxInstance());
|
var client = WebClient.create(VertxHolder.getVertxInstance());
|
||||||
var jsonObject = JsonObject.of("share_for_transfer", true);
|
var jsonObject = JsonObject.of("share_for_transfer", true);
|
||||||
|
|||||||
@@ -1,6 +0,0 @@
|
|||||||
package cn.qaiu.lz.common.util;
|
|
||||||
|
|
||||||
public class WoTools {
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,9 +1,6 @@
|
|||||||
package cn.qaiu.lz.web.http;
|
package cn.qaiu.lz.web.http;
|
||||||
|
|
||||||
import cn.qaiu.lz.common.util.CowTool;
|
import cn.qaiu.lz.common.util.*;
|
||||||
import cn.qaiu.lz.common.util.EcTool;
|
|
||||||
import cn.qaiu.lz.common.util.LzTool;
|
|
||||||
import cn.qaiu.lz.common.util.UcTool;
|
|
||||||
import cn.qaiu.lz.web.model.SysUser;
|
import cn.qaiu.lz.web.model.SysUser;
|
||||||
import cn.qaiu.lz.web.service.UserService;
|
import cn.qaiu.lz.web.service.UserService;
|
||||||
import cn.qaiu.vx.core.annotaions.RouteHandler;
|
import cn.qaiu.vx.core.annotaions.RouteHandler;
|
||||||
@@ -74,13 +71,20 @@ public class ServerApi {
|
|||||||
}).onFailure(t -> {
|
}).onFailure(t -> {
|
||||||
promise.fail(t.fillInStackTrace());
|
promise.fail(t.fillInStackTrace());
|
||||||
});
|
});
|
||||||
} else if (url.contains(UcTool.FULL_URL_PREFIX)) {
|
} else if (url.contains(UcTool.SHARE_URL_PREFIX)) {
|
||||||
UcTool.parse(url, pwd).onSuccess(resUrl -> {
|
UcTool.parse(url, pwd).onSuccess(resUrl -> {
|
||||||
response.putHeader("location", resUrl).setStatusCode(302).end();
|
response.putHeader("location", resUrl).setStatusCode(302).end();
|
||||||
promise.complete();
|
promise.complete();
|
||||||
}).onFailure(t -> {
|
}).onFailure(t -> {
|
||||||
promise.fail(t.fillInStackTrace());
|
promise.fail(t.fillInStackTrace());
|
||||||
});
|
});
|
||||||
|
} else if (url.contains(FjTool.SHARE_URL_PREFIX)) {
|
||||||
|
FjTool.parse(url).onSuccess(resUrl -> {
|
||||||
|
response.putHeader("location", resUrl).setStatusCode(302).end();
|
||||||
|
promise.complete();
|
||||||
|
}).onFailure(t -> {
|
||||||
|
promise.fail(t.fillInStackTrace());
|
||||||
|
});
|
||||||
}
|
}
|
||||||
return promise.future();
|
return promise.future();
|
||||||
}
|
}
|
||||||
@@ -155,4 +159,19 @@ public class ServerApi {
|
|||||||
}
|
}
|
||||||
return UcTool.parse(id, code);
|
return UcTool.parse(id, code);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@RouteMapping(value = "/fj/:id", method = RouteMethod.GET)
|
||||||
|
public void fjParse(HttpServerResponse response, String id) {
|
||||||
|
FjTool.parse(id).onSuccess(resUrl -> {
|
||||||
|
response.putHeader("location", resUrl).setStatusCode(302).end();
|
||||||
|
}).onFailure(t -> {
|
||||||
|
response.putHeader(CONTENT_TYPE, "text/html;charset=utf-8");
|
||||||
|
response.end(t.getMessage());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@RouteMapping(value = "/json/fj/:id", method = RouteMethod.GET)
|
||||||
|
public Future<String> fjParseJson(HttpServerResponse response, String id) {
|
||||||
|
return FjTool.parse(id);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
curl -F "file=@C:\Users\qaiu\Desktop\real\lz-web\web\src\main\resources\logback.xml" -i -XPOST 127.0.0.1:8088/demo/basePointApi/importTags
|
curl -F "file=@C:\Users\qaiu\Desktop\real\lz-web\web\src\main\resources\logback.xml" -i -XPOST 127.0.0.1:6400/demo/XXX/XXX
|
||||||
|
|
||||||
curl -F "file=@C:\Users\qaiu\Desktop\3.csv" -i -XPOST 127.0.0.1:8088/demo/basePointApi/importTags
|
curl -F "file=@C:\Users\qaiu\Desktop\3.csv" -i -XPOST 127.0.0.1:6400/demo/XXX/XXX
|
||||||
|
|||||||
@@ -1,22 +1,40 @@
|
|||||||
###
|
# 飞机盘分享URL=https://www.feijix.com/s/7jy0zlv
|
||||||
|
|
||||||
|
### step1 获取fid
|
||||||
# devType: 6
|
# devType: 6
|
||||||
# devModel: Chrome
|
# devModel: Chrome
|
||||||
# uuid: M_S4dl58NrtLkHCxonmWc
|
# uuid: M_S4dl58NrtLkHCxonmWc
|
||||||
# extra: 2
|
# extra: 2
|
||||||
# timestamp: C421C95B7A6E20745D98DD462118EEDA
|
# timestamp: C421C95B7A6E20745D98DD462118EEDA
|
||||||
# shareId: 146731
|
# shareId: 146731 ==> decode(7jy0zlv)
|
||||||
# type: 0
|
# type: 0
|
||||||
# offset: 1
|
# offset: 1
|
||||||
# limit: 60
|
# limit: 60
|
||||||
# @no-cookie-jar
|
# @no-cookie-jar
|
||||||
POST https://api.feijipan.com/ws/recommend/list?devType=6&devModel=Chrome&extra=2&shareId=146731&type=0&offset=1&limit=60
|
POST https://api.feijipan.com/ws/recommend/list?devType=6&devModel=Chrome&extra=2&shareId=146731&type=0&offset=1&limit=60
|
||||||
|
|
||||||
|
|
||||||
###
|
|
||||||
https://api.feijipan.com/ws/file/redirect?downloadId=4703B103BD6871F83441233393695EF3&enable=1&devType=6&uuid=z1PkRuW9of6Lg6_Y_NiNK×tamp=7FBE54506F49A38070E62936EA41D9B1&auth=159F4D596FC74050EACC77E2854CD2EDBF5F4A7B953DF72F95886E38F3DD9D3C
|
|
||||||
|
|
||||||
# 654488C79332E7279F9A38367BD72D3B
|
# 654488C79332E7279F9A38367BD72D3B
|
||||||
# 4703B103BD6871F83441233393695EF3
|
# 4703B103BD6871F83441233393695EF3
|
||||||
|
# https://web.feejii.com/files/2023/05/27/6/3975316/1459088526174041?filename=nginx-release-1.21.6.zip
|
||||||
|
|
||||||
|
### step2 获取下载直链Location
|
||||||
|
# AES123-ECB-PKCS7
|
||||||
|
# downloadId=fileIds| = 115206329|
|
||||||
|
# timestamp=1686215935703 B4C5B9833113ACA41F16AABADE17349C
|
||||||
|
# auth=115206329|1686215935703 = downloadId+timestamp
|
||||||
|
|
||||||
|
https://api.feijipan.com/ws/file/redirect?downloadId=4703B103BD6871F83441233393695EF3&enable=1&devType=6&uuid=M_S4dl58NrtLkHCxonmWc×tamp=B4C5B9833113ACA41F16AABADE17349C&auth=159F4D596FC74050EACC77E2854CD2ED3805DDAAF2AEA1EFC67BD687D22F19E3
|
||||||
|
|
||||||
|
###
|
||||||
|
# @no-redirect
|
||||||
|
https://api.feijipan.com/ws/file/redirect?downloadId=4703B103BD6871F83441233393695EF3&enable=1&devType=6&uuid=M_S4dl58NrtLkHCxonmWc×tamp=B3C7DBF6101393344EABFE1EEEAF8F91&auth=A872D9EB60313B2648F7F4233B3417FC5C7C136982E51B0D128EA7AF4EDC48F0
|
||||||
|
# Location: https://web.feejii.com/ee433d6e5102ad79af58a2aa0e6cf758/1686279412/files/2023/05/27/6/3975316/1459088526174041?filename=nginx-release-1.21.6.zip
|
||||||
|
# 11bc0a16dc31480812aa69bea1c20e2a ee433d6e5102ad79af58a2aa0e6cf758
|
||||||
|
|
||||||
|
|
||||||
|
### 下载直链
|
||||||
|
https://web.feejii.com/ee433d6e5102ad79af58a2aa0e6cf758/1686279412/files/2023/05/27/6/3975316/1459088526174041?filename=nginx-release-1.21.6.zip
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
###
|
###
|
||||||
https://api.feijipan.com/ws/file/redirect?downloadId=4703B103BD6871F83441233393695EF3&enable=1&devType=6&uuid=M_S4dl58NrtLkHCxonmWc×tamp=B4C5B9833113ACA41F16AABADE17349C&auth=159F4D596FC74050EACC77E2854CD2ED3805DDAAF2AEA1EFC67BD687D22F19E3
|
|
||||||
|
|||||||
@@ -93,9 +93,9 @@ Content-Type: application/json
|
|||||||
< ./request-form-data.json
|
< ./request-form-data.json
|
||||||
--WebAppBoundary--
|
--WebAppBoundary--
|
||||||
|
|
||||||
###
|
### https://www.ecpan.cn/web/#/yunpanProxy?path=%2F%23%2Fdrive%2Foutside&data=81027a5c99af5b11ca004966c945cce6W9Bf2&isShare=1
|
||||||
# @no-redirect
|
# @no-redirect
|
||||||
GET http://127.0.0.1:6400/parser?url=https://www.ecpan.cn/web/#/yunpanProxy?path=%2F%23%2Fdrive%2Foutside&data=81027a5c99af5b11ca004966c945cce6W9Bf2&isShare=
|
GET http://127.0.0.1:6400/parser?url=https://www.ecpan.cn/web/#/yunpanProxy?path=%2F%23%2Fdrive%2Foutside&data=81027a5c99af5b11ca004966c945cce6W9Bf2&isShare=1
|
||||||
# https://www.ecpan.cn/drive/fileextoverrid.do?chainUrlTemplate=https://www.ecpan.cn/web/#/yunpanProxy?path=%2F%23%2Fdrive%2Foutside&data=aa0cae0164d8885e6d35826b5b2901eckbWJBalM&parentId=-1
|
# https://www.ecpan.cn/drive/fileextoverrid.do?chainUrlTemplate=https://www.ecpan.cn/web/#/yunpanProxy?path=%2F%23%2Fdrive%2Foutside&data=aa0cae0164d8885e6d35826b5b2901eckbWJBalM&parentId=-1
|
||||||
|
|
||||||
###
|
###
|
||||||
@@ -127,3 +127,15 @@ action=downprocess&sign=AGYHOQk4BjdTWgQ7BzcGOlU_bATVSNQMxBDFQZgZoBj4HMFEgWnMOZ1I
|
|||||||
https://developer.lanzoug.com/file/?VDJbZVxtADFSWwY+U2YHa1FuU2tTYgBnBnUGZFNmWylSOVMpCTJQZQQhBSdQKQFkBjMOfAMyA21VPVs8V28BLVRkWyJcMAB6UjcGfVNlB29RZVN4U3YAawZxBiZTaVszUj5TYQkLUG0ENgVuUDUBMQZkDjsDbgMxVWBbZFc8ASZUMlt/XDwAZVIyBmFTNwc3UTtTYFM8ACQGcQZwUzJbaFJiUzYJZ1ArBGIFZlArATIGaQ4kAz8DMlVnWz9XMwE1VGZbOFw3AG1SPQY0UzcHMVFqUzdTbwBmBjEGMVNsW2NSM1MzCWxQNwRkBWVQNwEwBmQOPwNyA3tVO1stVy0BdVQnW2lccwA9UmAGbVM2BzZRPlNvUzgAOwY5BiZTe1szUj9TYQkyUDkEYwVjUDQBMAZsDiUDcgMnVTRbMVd8AT1UZVs6XDkAYVI0BmBTNQc/UTlTZVMqAHcGcQZ3UzJba1JkUzwJYVA0BGIFYVA0ATAGaw4tAykDaFUiW2BXOgExVGZbIlwzAGdSNgZ%2BUzYHMlE9U3hTOwA6
|
https://developer.lanzoug.com/file/?VDJbZVxtADFSWwY+U2YHa1FuU2tTYgBnBnUGZFNmWylSOVMpCTJQZQQhBSdQKQFkBjMOfAMyA21VPVs8V28BLVRkWyJcMAB6UjcGfVNlB29RZVN4U3YAawZxBiZTaVszUj5TYQkLUG0ENgVuUDUBMQZkDjsDbgMxVWBbZFc8ASZUMlt/XDwAZVIyBmFTNwc3UTtTYFM8ACQGcQZwUzJbaFJiUzYJZ1ArBGIFZlArATIGaQ4kAz8DMlVnWz9XMwE1VGZbOFw3AG1SPQY0UzcHMVFqUzdTbwBmBjEGMVNsW2NSM1MzCWxQNwRkBWVQNwEwBmQOPwNyA3tVO1stVy0BdVQnW2lccwA9UmAGbVM2BzZRPlNvUzgAOwY5BiZTe1szUj9TYQkyUDkEYwVjUDQBMAZsDiUDcgMnVTRbMVd8AT1UZVs6XDkAYVI0BmBTNQc/UTlTZVMqAHcGcQZ3UzJba1JkUzwJYVA0BGIFYVA0ATAGaw4tAykDaFUiW2BXOgExVGZbIlwzAGdSNgZ%2BUzYHMlE9U3hTOwA6
|
||||||
###
|
###
|
||||||
https://developer.lanzoug.com/file/?VTNVa1tqAjMFDAM7BDEAbAE+U2tfbgZhBnVbOQUwVCYEb1IoAToCNwQhVnRXLlcyVWAEdl9uVzkEbFYxVm5VeVVlVSxbNwJ4BWADeAQyAGgBNVN4X3oGbQZxW3sFP1Q8BGhSYAEDAj8ENlY9VzJXZ1U3BDFfMldlBDFWaVY9VXJVM1VxWzsCZwVlA2QEYAAwAWtTYF8wBiIGcVstBWRUZwQ0UjcBbwJ5BGJWNVcsV2RVOgQuX2NXZgQ2VjJWMlVhVWdVNlswAm8FagMxBGAANgE6UzdfYwZgBjFbbAU6VGwEZVIyAWQCZQRkVjZXMFdmVTcENV8uVy8EalYgVixVIVUmVWdbdAI/BTcDaARhADEBblNvXzQGPQY5W3sFLVQ8BGlSYAE6AmsEY1Y2VztXY1U/BDFfM1dnBDRWZFYkVXpVc1VkW2oCIQVuA2QEZgA5AW5TZl8wBjYGMFtkBWFUcwRxUnUBKwJrBGNWNlc7V2NVPwQxXzJXZQQzVmdWLFUhVTxVcls7AmcFYgNnBH4AMwFoU2RfLgY1BjVbawV3VGIEPA==
|
https://developer.lanzoug.com/file/?VTNVa1tqAjMFDAM7BDEAbAE+U2tfbgZhBnVbOQUwVCYEb1IoAToCNwQhVnRXLlcyVWAEdl9uVzkEbFYxVm5VeVVlVSxbNwJ4BWADeAQyAGgBNVN4X3oGbQZxW3sFP1Q8BGhSYAEDAj8ENlY9VzJXZ1U3BDFfMldlBDFWaVY9VXJVM1VxWzsCZwVlA2QEYAAwAWtTYF8wBiIGcVstBWRUZwQ0UjcBbwJ5BGJWNVcsV2RVOgQuX2NXZgQ2VjJWMlVhVWdVNlswAm8FagMxBGAANgE6UzdfYwZgBjFbbAU6VGwEZVIyAWQCZQRkVjZXMFdmVTcENV8uVy8EalYgVixVIVUmVWdbdAI/BTcDaARhADEBblNvXzQGPQY5W3sFLVQ8BGlSYAE6AmsEY1Y2VztXY1U/BDFfM1dnBDRWZFYkVXpVc1VkW2oCIQVuA2QEZgA5AW5TZl8wBjYGMFtkBWFUcwRxUnUBKwJrBGNWNlc7V2NVPwQxXzJXZQQzVmdWLFUhVTxVcls7AmcFYgNnBH4AMwFoU2RfLgY1BjVbawV3VGIEPA==
|
||||||
|
|
||||||
|
|
||||||
|
###
|
||||||
|
# @no-redirect
|
||||||
|
GET http://127.0.0.1:6400/parser?url=https://www.feijix.com/s/tIfhRqH
|
||||||
|
|
||||||
|
###
|
||||||
|
GET http://127.0.0.1:6400/json/fj/tIfhRqH
|
||||||
|
|
||||||
|
###
|
||||||
|
# @no-redirect
|
||||||
|
GET http://127.0.0.1:6400/fj/tIfhRqH
|
||||||
|
|||||||
57
web-service/src/test/java/cn/qaiu/web/test/TestAESUtil.java
Normal file
57
web-service/src/test/java/cn/qaiu/web/test/TestAESUtil.java
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
package cn.qaiu.web.test;
|
||||||
|
|
||||||
|
import cn.qaiu.lz.common.util.AESUtils;
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import javax.crypto.BadPaddingException;
|
||||||
|
import javax.crypto.IllegalBlockSizeException;
|
||||||
|
import javax.crypto.NoSuchPaddingException;
|
||||||
|
import java.security.InvalidKeyException;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import java.util.HexFormat;
|
||||||
|
|
||||||
|
public class TestAESUtil {
|
||||||
|
|
||||||
|
// 1686215935703
|
||||||
|
// B4C5B9833113ACA41F16AABADE17349C
|
||||||
|
@Test
|
||||||
|
public void decode() throws NoSuchPaddingException, IllegalBlockSizeException, NoSuchAlgorithmException,
|
||||||
|
BadPaddingException, InvalidKeyException {
|
||||||
|
String hex = AESUtils.encryptHexByAES("1686215935703", AESUtils.CIPHER_AES2);
|
||||||
|
Assert.assertEquals("B4C5B9833113ACA41F16AABADE17349C", hex.toUpperCase());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void encode() throws IllegalBlockSizeException, NoSuchPaddingException, BadPaddingException,
|
||||||
|
NoSuchAlgorithmException, InvalidKeyException {
|
||||||
|
String source = AESUtils.decryptByHexAES("B4C5B9833113ACA41F16AABADE17349C", AESUtils.CIPHER_AES2);
|
||||||
|
Assert.assertEquals("1686215935703", source);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void toHex() {
|
||||||
|
byte[] d234EF67A1s = HexFormat.of().parseHex("D234EF67A1");
|
||||||
|
Assert.assertArrayEquals(new byte[]{(byte) 0xd2, (byte) 0x34, (byte) 0xef, (byte) 0x67, (byte) 0xa1},
|
||||||
|
d234EF67A1s);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void base64AES() throws NoSuchAlgorithmException {
|
||||||
|
System.out.println(HexFormat.of().formatHex(AESUtils.createKeyString(AESUtils.KEY_SIZE_128_LENGTH).getEncoded()));
|
||||||
|
System.out.println(HexFormat.of().formatHex(AESUtils.createKeyString(AESUtils.KEY_SIZE_192_LENGTH).getEncoded()));
|
||||||
|
System.out.println(HexFormat.of().formatHex(AESUtils.createKeyString(AESUtils.KEY_SIZE_256_LENGTH).getEncoded()));
|
||||||
|
|
||||||
|
// TODO Base64-AES
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testIdDecode() {
|
||||||
|
Assert.assertEquals(146731, AESUtils.idEncrypt("7jy0zlv"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testTs() {
|
||||||
|
System.out.println(System.currentTimeMillis());
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user