mirror of
https://github.com/NotoChen/Jetbrains-Help.git
synced 2025-05-24 15:06:43 +08:00
Merge 4b4f81f01b
into 882dbfc126
This commit is contained in:
commit
db04ba66e3
33
README.md
33
README.md
|
@ -53,20 +53,21 @@
|
|||
|
||||
### 功能列表
|
||||
|
||||
| 功能 | DID |
|
||||
|:-------------------------|:---:|
|
||||
| Jetbrains全产品支持 | ✅ |
|
||||
| Jetbrains全插件支持 | ✅ |
|
||||
| 插件库全自动订阅官网更新 | ✅ |
|
||||
| 公私钥/证书, 自动生成管理 | ✅ |
|
||||
| power.conf文件自动配置 | ✅ |
|
||||
| ja-netfilter.zip自动打包 | ✅ |
|
||||
| 自定义License Show | ✅ |
|
||||
| 支持实时搜索 | ✅ |
|
||||
| 插件默认按名称排序 | ✅ |
|
||||
| 支持local/jar/dockerfile运行 | ✅ |
|
||||
| 单码全家桶激活支持 | ✅ |
|
||||
| …… | ☑️ |
|
||||
| 功能 | DID |
|
||||
|:---------------------------|:---:|
|
||||
| Jetbrains全产品支持 | ✅ |
|
||||
| Jetbrains全插件支持 | ✅ |
|
||||
| 插件库全自动订阅官网更新 | ✅ |
|
||||
| 公私钥/证书, 自动生成管理 | ✅ |
|
||||
| power.conf文件自动配置 | ✅ |
|
||||
| ja-netfilter.zip自动打包 | ✅ |
|
||||
| 自定义License Show | ✅ |
|
||||
| 支持实时搜索 | ✅ |
|
||||
| 插件默认按名称排序 | ✅ |
|
||||
| 支持local/jar/dockerfile运行 | ✅ |
|
||||
| 单码全家桶激活支持 | ✅ |
|
||||
| Jetbrains License Server支持 | ✅ |
|
||||
| …… | ☑️ |
|
||||
|
||||
## 运行教程
|
||||
|
||||
|
@ -139,7 +140,7 @@
|
|||
- **点击** `编辑自定义虚拟机选型`
|
||||
- **键入** 如下配置
|
||||
```
|
||||
-javaagent:you-path/ja-netfilter.jar
|
||||
-javaagent:you-path/ja-netfilter.jar=jetbrains
|
||||
--add-opens=java.base/jdk.internal.org.objectweb.asm=ALL-UNNAMED
|
||||
--add-opens=java.base/jdk.internal.org.objectweb.asm.tree=ALL-UNNAMED
|
||||
```
|
||||
|
@ -157,7 +158,7 @@
|
|||
- **点击** `编辑JVM选项`
|
||||
- **键入** 如下配置
|
||||
```
|
||||
-javaagent:you-path/ja-netfilter.jar
|
||||
-javaagent:you-path/ja-netfilter.jar=jetbrains
|
||||
--add-opens=java.base/jdk.internal.org.objectweb.asm=ALL-UNNAMED
|
||||
--add-opens=java.base/jdk.internal.org.objectweb.asm.tree=ALL-UNNAMED
|
||||
```
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
package com.jetbrains.help;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.io.IoUtil;
|
||||
import cn.hutool.core.net.Ipv4Util;
|
||||
import cn.hutool.core.text.CharSequenceUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.crypto.SecureUtil;
|
||||
import cn.hutool.extra.spring.SpringUtil;
|
||||
import com.jetbrains.help.context.*;
|
||||
import lombok.SneakyThrows;
|
||||
|
@ -17,6 +19,7 @@ import org.springframework.scheduling.annotation.EnableScheduling;
|
|||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
|
||||
import java.net.InetAddress;
|
||||
import java.security.cert.Certificate;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
|
@ -37,7 +40,6 @@ public class JetbrainsHelpApplication {
|
|||
PluginsContextHolder.init();
|
||||
CertificateContextHolder.init();
|
||||
AgentContextHolder.init();
|
||||
|
||||
InetAddress localHost = InetAddress.getLocalHost();
|
||||
String address = CharSequenceUtil.format("http://{}:{}", localHost.getHostAddress(), SpringUtil.getProperty("server.port"));
|
||||
String runSuccessWarn = "\n====================================================================================\n" +
|
||||
|
|
|
@ -26,7 +26,7 @@ public class AgentContextHolder {
|
|||
|
||||
private static final String JA_NETFILTER_FILE_PATH = "external/agent/ja-netfilter";
|
||||
|
||||
private static final String POWER_CONF_FILE_NAME = JA_NETFILTER_FILE_PATH + "/config/power.conf";
|
||||
private static final String POWER_CONF_FILE_NAME = JA_NETFILTER_FILE_PATH + "/config-jetbrains/power.conf";
|
||||
|
||||
private static File jaNetfilterFile;
|
||||
|
||||
|
@ -35,7 +35,7 @@ public class AgentContextHolder {
|
|||
public static void init() {
|
||||
log.info("Agent context init loading...");
|
||||
jaNetfilterZipFile = FileTools.getFileOrCreat(JA_NETFILTER_FILE_PATH + ".zip");
|
||||
if (!FileTools.fileExists(JA_NETFILTER_FILE_PATH)) {
|
||||
if (FileTools.fileNotExists(JA_NETFILTER_FILE_PATH)) {
|
||||
unzipJaNetfilter();
|
||||
if (!powerConfHasInit()) {
|
||||
log.info("Agent config init loading...");
|
||||
|
@ -75,14 +75,27 @@ public class AgentContextHolder {
|
|||
|
||||
@SneakyThrows
|
||||
private static String generatePowerConfigRule() {
|
||||
X509Certificate crt = (X509Certificate) KeyUtil.readX509Certificate(IoUtil.toStream(CertificateContextHolder.crtFile()));
|
||||
RSAPublicKey publicKey = (RSAPublicKey) PemUtil.readPemPublicKey(IoUtil.toStream(CertificateContextHolder.publicKeyFile()));
|
||||
RSAPublicKey rootPublicKey = (RSAPublicKey) PemUtil.readPemPublicKey(IoUtil.toStream(CertificateContextHolder.rootKeyFile()));
|
||||
StringBuilder result = new StringBuilder();
|
||||
X509Certificate crt = (X509Certificate) KeyUtil.readX509Certificate(IoUtil.toStream(CertificateContextHolder.getCrtFile()));
|
||||
X509Certificate licenseCrt = (X509Certificate) KeyUtil.readX509Certificate(IoUtil.toStream(CertificateContextHolder.getLicenseCrtFile()));
|
||||
RSAPublicKey publicKey = (RSAPublicKey) PemUtil.readPemPublicKey(IoUtil.toStream(CertificateContextHolder.getPublicKeyFile()));
|
||||
RSAPublicKey rootPublicKey = (RSAPublicKey) PemUtil.readPemPublicKey(IoUtil.toStream(CertificateContextHolder.getRootKeyFile()));
|
||||
RSAPublicKey rootLicensePublicKey = (RSAPublicKey) PemUtil.readPemPublicKey(IoUtil.toStream(CertificateContextHolder.getRootLicenseKeyFile()));
|
||||
BigInteger x = new BigInteger(1, crt.getSignature());
|
||||
BigInteger y = BigInteger.valueOf(65537L);
|
||||
BigInteger y = rootPublicKey.getPublicExponent();
|
||||
BigInteger z = rootPublicKey.getModulus();
|
||||
BigInteger r = x.modPow(publicKey.getPublicExponent(), publicKey.getModulus());
|
||||
return CharSequenceUtil.format("EQUAL,{},{},{}->{}", x, y, z, r);
|
||||
result.append("; Activation Code").append("\n")
|
||||
.append(CharSequenceUtil.format("EQUAL,{},{},{}->{}", x, y, z, r))
|
||||
.append("\n");
|
||||
x = new BigInteger(1, licenseCrt.getSignature());
|
||||
z = rootLicensePublicKey.getModulus();
|
||||
y = rootLicensePublicKey.getPublicExponent();
|
||||
r = x.modPow(publicKey.getPublicExponent(), publicKey.getModulus());
|
||||
result.append("; License Server").append("\n")
|
||||
.append(CharSequenceUtil.format("EQUAL,{},{},{}->{}", x, y, z, r))
|
||||
.append("\n");
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
private static String generatePowerConfigStr(String ruleValue) {
|
||||
|
|
|
@ -3,10 +3,12 @@ package com.jetbrains.help.context;
|
|||
import cn.hutool.core.date.DateField;
|
||||
import cn.hutool.core.date.DateUtil;
|
||||
import cn.hutool.core.io.FileUtil;
|
||||
import cn.hutool.core.io.IoUtil;
|
||||
import cn.hutool.crypto.PemUtil;
|
||||
import cn.hutool.crypto.SecureUtil;
|
||||
import com.jetbrains.help.util.FileTools;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.bouncycastle.asn1.x500.X500Name;
|
||||
|
@ -27,29 +29,47 @@ import java.security.cert.Certificate;
|
|||
import java.security.cert.CertificateEncodingException;
|
||||
import java.security.cert.CertificateException;
|
||||
|
||||
/**
|
||||
* <a href="https://github.com/JetBrains/marketplace-makemecoffee-plugin/blob/master/src/main/java/com/company/license/CheckLicense.java">源证书来源</a>
|
||||
*/
|
||||
@Slf4j
|
||||
@NoArgsConstructor(access = AccessLevel.PRIVATE)
|
||||
public class CertificateContextHolder {
|
||||
|
||||
private static final String ROOT_KEY_FILE_NAME = "external/certificate/root.key";
|
||||
private static final String ROOT_LICENSE_KEY_FILE_NAME = "external/certificate/license-root.key";
|
||||
private static final String PRIVATE_KEY_FILE_NAME = "external/certificate/private.key";
|
||||
private static final String PUBLIC_KEY_FILE_NAME = "external/certificate/public.key";
|
||||
private static final String CET_FILE_NAME = "external/certificate/ca.crt";
|
||||
private static final String LICENSE_CET_FILE_NAME = "external/certificate/license-ca.crt";
|
||||
public static final String SERVER_UID = "cikaros.top";
|
||||
|
||||
@Getter
|
||||
private static File rootKeyFile;
|
||||
|
||||
@Getter
|
||||
private static File rootLicenseKeyFile;
|
||||
|
||||
@Getter
|
||||
private static File privateKeyFile;
|
||||
|
||||
@Getter
|
||||
private static File publicKeyFile;
|
||||
|
||||
@Getter
|
||||
private static File crtFile;
|
||||
|
||||
@Getter
|
||||
private static File licenseCrtFile;
|
||||
|
||||
public static void init() {
|
||||
log.info("certificate context init loading...");
|
||||
rootKeyFile = FileTools.getFileOrCreat(ROOT_KEY_FILE_NAME);
|
||||
if (!FileTools.fileExists(PRIVATE_KEY_FILE_NAME)
|
||||
|| !FileTools.fileExists(PUBLIC_KEY_FILE_NAME)
|
||||
|| !FileTools.fileExists(CET_FILE_NAME)) {
|
||||
rootLicenseKeyFile = FileTools.getFileOrCreat(ROOT_LICENSE_KEY_FILE_NAME);
|
||||
if (FileTools.fileNotExists(PRIVATE_KEY_FILE_NAME)
|
||||
|| FileTools.fileNotExists(PUBLIC_KEY_FILE_NAME)
|
||||
|| FileTools.fileNotExists(CET_FILE_NAME)
|
||||
|| FileTools.fileNotExists(LICENSE_CET_FILE_NAME)) {
|
||||
log.info("certificate context generate loading...");
|
||||
generateCertificate();
|
||||
log.info("certificate context generate success!");
|
||||
|
@ -57,27 +77,11 @@ public class CertificateContextHolder {
|
|||
privateKeyFile = FileTools.getFileOrCreat(PRIVATE_KEY_FILE_NAME);
|
||||
publicKeyFile = FileTools.getFileOrCreat(PUBLIC_KEY_FILE_NAME);
|
||||
crtFile = FileTools.getFileOrCreat(CET_FILE_NAME);
|
||||
licenseCrtFile = FileTools.getFileOrCreat(LICENSE_CET_FILE_NAME);
|
||||
}
|
||||
log.info("certificate context init success !");
|
||||
}
|
||||
|
||||
|
||||
public static File rootKeyFile() {
|
||||
return CertificateContextHolder.rootKeyFile;
|
||||
}
|
||||
|
||||
public static File privateKeyFile() {
|
||||
return CertificateContextHolder.privateKeyFile;
|
||||
}
|
||||
|
||||
public static File publicKeyFile() {
|
||||
return CertificateContextHolder.publicKeyFile;
|
||||
}
|
||||
|
||||
public static File crtFile() {
|
||||
return CertificateContextHolder.crtFile;
|
||||
}
|
||||
|
||||
public static void generateCertificate() {
|
||||
KeyPair keyPair = SecureUtil.generateKeyPair("RSA", 4096);
|
||||
PrivateKey privateKey = keyPair.getPrivate();
|
||||
|
@ -86,16 +90,8 @@ public class CertificateContextHolder {
|
|||
PemUtil.writePemObject("PRIVATE KEY", privateKey.getEncoded(), FileUtil.getWriter(privateKeyFile, StandardCharsets.UTF_8, false));
|
||||
publicKeyFile = FileTools.getFileOrCreat(PUBLIC_KEY_FILE_NAME);
|
||||
PemUtil.writePemObject("PUBLIC KEY", publicKey.getEncoded(), FileUtil.getWriter(publicKeyFile, StandardCharsets.UTF_8, false));
|
||||
JcaX509v3CertificateBuilder certificateBuilder = new JcaX509v3CertificateBuilder(
|
||||
new X500Name("CN=JetProfile CA"),
|
||||
BigInteger.valueOf(System.currentTimeMillis()),
|
||||
DateUtil.yesterday(),
|
||||
DateUtil.date().offset(DateField.YEAR, 100),
|
||||
new X500Name("CN=Jetbrains-Help"),
|
||||
SubjectPublicKeyInfo.getInstance(publicKey.getEncoded()));
|
||||
try {
|
||||
ContentSigner signer = new JcaContentSignerBuilder("SHA256withRSA").build(privateKey);
|
||||
Certificate certificate = new JcaX509CertificateConverter().setProvider("BC").getCertificate(certificateBuilder.build(signer));
|
||||
Certificate certificate = generateCertificate(keyPair,"JetProfile CA","Jetbrains-Help");
|
||||
crtFile = FileTools.getFileOrCreat(CET_FILE_NAME);
|
||||
PemUtil.writePemObject("CERTIFICATE", certificate.getEncoded(), FileUtil.getWriter(crtFile, StandardCharsets.UTF_8, false));
|
||||
} catch (OperatorCreationException e) {
|
||||
|
@ -105,7 +101,33 @@ public class CertificateContextHolder {
|
|||
} catch (CertificateException e) {
|
||||
throw new IllegalArgumentException("The certificate read exception", e);
|
||||
}
|
||||
//TODO 创建License Servers CA
|
||||
try {
|
||||
Certificate certificate = generateCertificate(keyPair,"License Servers CA",String.format("%s.lsrv.jetbrains.com", SERVER_UID));
|
||||
licenseCrtFile = FileTools.getFileOrCreat(LICENSE_CET_FILE_NAME);
|
||||
PemUtil.writePemObject("CERTIFICATE", certificate.getEncoded(), FileUtil.getWriter(licenseCrtFile, StandardCharsets.UTF_8, false));
|
||||
} catch (OperatorCreationException e) {
|
||||
throw new IllegalArgumentException("Certificate operator creation exception", e);
|
||||
} catch (CertificateEncodingException e) {
|
||||
throw new IllegalArgumentException("The certificate encoding exception", e);
|
||||
} catch (CertificateException e) {
|
||||
throw new IllegalArgumentException("The certificate read exception", e);
|
||||
}
|
||||
}
|
||||
|
||||
private static Certificate generateCertificate(KeyPair keyPair, String issuer, String subject) throws OperatorCreationException, CertificateException {
|
||||
PrivateKey privateKey = keyPair.getPrivate();
|
||||
PublicKey publicKey = keyPair.getPublic();
|
||||
JcaX509v3CertificateBuilder certificateBuilder = new JcaX509v3CertificateBuilder(
|
||||
new X500Name(String.format("CN=%s", issuer)),
|
||||
BigInteger.valueOf(System.currentTimeMillis()),
|
||||
DateUtil.yesterday(),
|
||||
DateUtil.date().offset(DateField.YEAR, 100),
|
||||
new X500Name(String.format("CN=%s", subject)),
|
||||
SubjectPublicKeyInfo.getInstance(publicKey.getEncoded()));
|
||||
ContentSigner signer = new JcaContentSignerBuilder("SHA256withRSA").build(privateKey);
|
||||
return new JcaX509CertificateConverter().setProvider("BC").getCertificate(certificateBuilder.build(signer));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,20 +1,24 @@
|
|||
package com.jetbrains.help.context;
|
||||
|
||||
import cn.hutool.core.codec.Base64;
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.io.IoUtil;
|
||||
import cn.hutool.core.text.CharSequenceUtil;
|
||||
import cn.hutool.core.util.IdUtil;
|
||||
import cn.hutool.core.util.XmlUtil;
|
||||
import cn.hutool.crypto.PemUtil;
|
||||
import cn.hutool.crypto.SecureUtil;
|
||||
import cn.hutool.crypto.SignUtil;
|
||||
import cn.hutool.crypto.asymmetric.Sign;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.Node;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.PublicKey;
|
||||
import java.security.cert.Certificate;
|
||||
|
@ -22,7 +26,7 @@ import java.security.cert.CertificateEncodingException;
|
|||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import static cn.hutool.crypto.asymmetric.SignAlgorithm.SHA1withRSA;
|
||||
import static cn.hutool.crypto.asymmetric.SignAlgorithm.*;
|
||||
|
||||
@Slf4j
|
||||
@NoArgsConstructor(access = AccessLevel.PRIVATE)
|
||||
|
@ -43,9 +47,9 @@ public class LicenseContextHolder {
|
|||
.setProducts(products);
|
||||
String licensePartJson = JSONUtil.toJsonStr(licensePart);
|
||||
String licensePartBase64 = Base64.encode(licensePartJson);
|
||||
PrivateKey privateKey = PemUtil.readPemPrivateKey(IoUtil.toStream(CertificateContextHolder.privateKeyFile()));
|
||||
PublicKey publicKey = PemUtil.readPemPublicKey(IoUtil.toStream(CertificateContextHolder.publicKeyFile()));
|
||||
Certificate certificate = SecureUtil.readX509Certificate(IoUtil.toStream(CertificateContextHolder.crtFile()));
|
||||
PrivateKey privateKey = PemUtil.readPemPrivateKey(IoUtil.toStream(CertificateContextHolder.getPrivateKeyFile()));
|
||||
PublicKey publicKey = PemUtil.readPemPublicKey(IoUtil.toStream(CertificateContextHolder.getPublicKeyFile()));
|
||||
Certificate certificate = SecureUtil.readX509Certificate(IoUtil.toStream(CertificateContextHolder.getCrtFile()));
|
||||
Sign sign = SignUtil.sign(SHA1withRSA, privateKey.getEncoded(), publicKey.getEncoded());
|
||||
String signatureBase64 = Base64.encode(sign.sign(licensePartJson));
|
||||
String certBase64;
|
||||
|
@ -74,4 +78,147 @@ public class LicenseContextHolder {
|
|||
private String paidUpTo;
|
||||
}
|
||||
|
||||
public static String obtainTicket(ObtainTicketRequest request) {
|
||||
PrivateKey privateKey = PemUtil.readPemPrivateKey(IoUtil.toStream(CertificateContextHolder.getPrivateKeyFile()));
|
||||
PublicKey publicKey = PemUtil.readPemPublicKey(IoUtil.toStream(CertificateContextHolder.getPublicKeyFile()));
|
||||
Certificate cert = SecureUtil.readX509Certificate(IoUtil.toStream(CertificateContextHolder.getCrtFile()));
|
||||
Certificate licenseCert = SecureUtil.readX509Certificate(IoUtil.toStream(CertificateContextHolder.getLicenseCrtFile()));
|
||||
ObtainTicketResponse response = obtainTicket(privateKey, publicKey, cert, licenseCert, request);
|
||||
Document document = XmlUtil.beanToXml(response);
|
||||
String result = XmlUtil.toStr(document, "UTF-8", true, true);
|
||||
//!important 必须去除XML中所有的换行和空格,即紧凑模式
|
||||
result = result.replaceAll("[\n ]", "");
|
||||
Sign sign = SignUtil.sign(SHA1withRSA, privateKey.getEncoded(), publicKey.getEncoded());
|
||||
String sign64 = Base64.encode(sign.sign(result));
|
||||
String licenseCertBase64;
|
||||
try {
|
||||
licenseCertBase64 = Base64.encode(licenseCert.getEncoded());
|
||||
} catch (CertificateEncodingException e) {
|
||||
throw new IllegalArgumentException("Certificate extraction failed", e);
|
||||
}
|
||||
//<!-- SHA1withRSA-{下面 xml 主体内容的签名 }-{license Server CA} -->
|
||||
String signRes = String.format("<!-- SHA1withRSA-%s-%s -->", sign64, licenseCertBase64);
|
||||
return signRes + "\n" + result;
|
||||
}
|
||||
|
||||
public static ObtainTicketResponse obtainTicket(PrivateKey privateKey, PublicKey publicKey, Certificate cert, Certificate licenseCert, ObtainTicketRequest request) {
|
||||
String certBase64;
|
||||
String licenseCertBase64;
|
||||
try {
|
||||
certBase64 = Base64.encode(cert.getEncoded());
|
||||
licenseCertBase64 = Base64.encode(licenseCert.getEncoded());
|
||||
} catch (CertificateEncodingException e) {
|
||||
throw new IllegalArgumentException("Certificate extraction failed", e);
|
||||
}
|
||||
|
||||
long time = System.currentTimeMillis();
|
||||
long leaseTime = time + (1000L * 60 * 60 * 24 * 365);
|
||||
//{失效时间戳}:{serverUid}
|
||||
String serverLease = String.format("%d:%s", leaseTime, CertificateContextHolder.SERVER_UID);
|
||||
|
||||
Sign sign = SignUtil.sign(SHA1withRSA, privateKey.getEncoded(), publicKey.getEncoded());
|
||||
String signatureBase64 = Base64.encode(sign.sign(String.format("%d:%s", time, request.getMachineId())));
|
||||
//{时间戳}:{客户端提交的machineId}:SHA1withRSA:{对({时间戳}:{客户端提交的machineId})的签名}:{license Server CA}
|
||||
String confirmationStamp = String.format("%d:%s:SHA1withRSA:%s:%s",
|
||||
time, request.getMachineId(), signatureBase64, licenseCertBase64);
|
||||
|
||||
sign = SignUtil.sign(SHA512withRSA, privateKey.getEncoded(), publicKey.getEncoded());
|
||||
byte[] bytes = sign.sign(serverLease);
|
||||
signatureBase64 = Base64.encode(bytes);
|
||||
//SHA512withRSA-{对「serverLease」的值进行签名}-{JetProfile CA}
|
||||
String leaseSignature = String.format("SHA512withRSA-%s-%s", signatureBase64, certBase64);
|
||||
// String leaseSignature = new BigInteger(1,bytes).toString();
|
||||
return ObtainTicketResponse.builder()
|
||||
.action(Action.NONE)
|
||||
.responseCode(ResponseCode.OK)
|
||||
.message(ResponseCode.OK.name())
|
||||
.ticketId("20")
|
||||
.ticketProperties(String.format("licensee=%s\tlicenseType=4\tmetadata=0120230914PSAX000005", request.getUserName()))
|
||||
.validationPeriod(71829000L)
|
||||
.validationDeadlinePeriod(-1)
|
||||
.prolongationPeriod(63462000L)//有效期
|
||||
.salt(request.getSalt())
|
||||
.serverUid(CertificateContextHolder.SERVER_UID)
|
||||
.serverLease(serverLease)
|
||||
.confirmationStamp(confirmationStamp)
|
||||
.leaseSignature(leaseSignature)
|
||||
.build();
|
||||
}
|
||||
|
||||
public enum Action {
|
||||
NONE,
|
||||
}
|
||||
|
||||
public enum ResponseCode {
|
||||
OK
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取认证票据
|
||||
*
|
||||
* @author Cikaros
|
||||
* @date 2023/11/1
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
public static class ObtainTicketResponse {
|
||||
@Builder.Default
|
||||
private Action action = Action.NONE;
|
||||
private String confirmationStamp;
|
||||
private String leaseSignature;
|
||||
@Builder.Default
|
||||
private String message = "";
|
||||
@Builder.Default
|
||||
private Long prolongationPeriod = 1800000L;
|
||||
@Builder.Default
|
||||
private ResponseCode responseCode = ResponseCode.OK;
|
||||
private String salt;
|
||||
private String serverLease;
|
||||
private String serverUid;
|
||||
@Builder.Default
|
||||
private String ticketId = "1";
|
||||
private String ticketProperties;
|
||||
@Builder.Default
|
||||
private Integer validationDeadlinePeriod = -1;
|
||||
@Builder.Default
|
||||
private Long validationPeriod = 1800000L;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @author Cikaros
|
||||
* @date 2023/10/20
|
||||
*/
|
||||
@Data
|
||||
public static class ObtainTicketRequest {
|
||||
|
||||
private String ideProductCode;
|
||||
|
||||
private Boolean empty;
|
||||
|
||||
private String buildDate;
|
||||
|
||||
private String buildNumber;
|
||||
|
||||
private Integer clientVersion;
|
||||
|
||||
private String hostName;
|
||||
|
||||
private String machineId;
|
||||
|
||||
private String productCode;
|
||||
|
||||
private String productFamilyId;
|
||||
|
||||
private String salt;
|
||||
|
||||
private Boolean secure;
|
||||
|
||||
private String userName;
|
||||
|
||||
private String version;
|
||||
|
||||
private Long versionNumber;
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
package com.jetbrains.help.controller;
|
||||
|
||||
import com.jetbrains.help.context.LicenseContextHolder;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
|
||||
@Controller
|
||||
@RequestMapping("/rpc")
|
||||
public class ObtainTicketApiController {
|
||||
|
||||
@GetMapping(value = "/obtainTicket.action", produces = {"application/xml;charset=utf-8"})
|
||||
@ResponseBody
|
||||
public ResponseEntity<String> obtainTicket(LicenseContextHolder.ObtainTicketRequest request) {
|
||||
return ResponseEntity.ok()
|
||||
.contentType(MediaType.APPLICATION_XML)
|
||||
.body(LicenseContextHolder.obtainTicket(request));
|
||||
}
|
||||
}
|
|
@ -4,15 +4,19 @@ import cn.hutool.core.io.FileUtil;
|
|||
import cn.hutool.core.io.IoUtil;
|
||||
import cn.hutool.core.text.CharSequenceUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.crypto.digest.DigestUtil;
|
||||
import com.jetbrains.help.JetbrainsHelpApplication;
|
||||
import com.jetbrains.help.context.AgentContextHolder;
|
||||
import com.jetbrains.help.context.PluginsContextHolder;
|
||||
import com.jetbrains.help.context.ProductsContextHolder;
|
||||
import com.jetbrains.help.properties.JetbrainsHelpProperties;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.cglib.core.Local;
|
||||
import org.springframework.core.io.InputStreamResource;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.http.HttpRequest;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.stereotype.Controller;
|
||||
|
@ -20,11 +24,13 @@ import org.springframework.ui.Model;
|
|||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
import org.thymeleaf.util.DateUtils;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.List;
|
||||
import java.util.*;
|
||||
|
||||
import static org.springframework.http.HttpHeaders.CONTENT_DISPOSITION;
|
||||
import static org.springframework.http.MediaType.APPLICATION_JSON;
|
||||
import static org.springframework.http.MediaType.APPLICATION_OCTET_STREAM;
|
||||
|
||||
@Controller
|
||||
|
@ -34,15 +40,37 @@ public class IndexController {
|
|||
private final JetbrainsHelpProperties jetbrainsHelpProperties;
|
||||
|
||||
@GetMapping
|
||||
public String index(Model model) {
|
||||
public String index(HttpServletRequest request,Model model) {
|
||||
String basePath = (request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + request.getContextPath());
|
||||
List<ProductsContextHolder.ProductCache> productCacheList = ProductsContextHolder.productCacheList();
|
||||
List<PluginsContextHolder.PluginCache> pluginCacheList = PluginsContextHolder.pluginCacheList();
|
||||
model.addAttribute("products", productCacheList);
|
||||
model.addAttribute("plugins", pluginCacheList);
|
||||
model.addAttribute("defaults", jetbrainsHelpProperties);
|
||||
model.addAttribute("basePath", basePath);
|
||||
return "index";
|
||||
}
|
||||
|
||||
@GetMapping("/scoop/ja-netfilter")
|
||||
@ResponseBody
|
||||
public ResponseEntity<Map<String,Object>> scoopInstall(HttpServletRequest request) {
|
||||
String basePath = (request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + request.getContextPath());
|
||||
Map<String, Object> json = new HashMap<>();
|
||||
String version = DateUtils.format(new Date(),"yyyyMMdd", Locale.getDefault());
|
||||
json.put("version", version);
|
||||
json.put("description", "JetBrains' dragon slayer");
|
||||
json.put("homepage", "https://cikaros.top");
|
||||
json.put("license", "MIT");
|
||||
json.put("url", String.format("%s/ja-netfilter#dl.zip", basePath));
|
||||
json.put("extract_to", Arrays.asList("", "config-jetbrains", "plugins-jetbrains", "scripts", "vmoptions"));
|
||||
json.put("hash", DigestUtil.sha256Hex(AgentContextHolder.jaNetfilterZipFile()));
|
||||
json.put("post_install", "cscript $dir/scripts/install-current-user.vbs");
|
||||
json.put("pre_uninstall", "cscript $dir/scripts/uninstall-current-user.vbs");
|
||||
return ResponseEntity.ok()
|
||||
.contentType(APPLICATION_JSON)
|
||||
.body(json);
|
||||
}
|
||||
|
||||
@GetMapping("search")
|
||||
public String index(@RequestParam(required = false) String search, Model model) {
|
||||
List<ProductsContextHolder.ProductCache> productCacheList = ProductsContextHolder.productCacheList();
|
||||
|
@ -70,4 +98,5 @@ public class IndexController {
|
|||
.contentType(APPLICATION_OCTET_STREAM)
|
||||
.body(new InputStreamResource(FileUtil.getInputStream(jaNetfilterZipFile)));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -3,12 +3,10 @@ package com.jetbrains.help.util;
|
|||
import cn.hutool.core.io.FileUtil;
|
||||
import cn.hutool.core.text.CharSequenceUtil;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import org.springframework.boot.system.ApplicationHome;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
public interface FileTools {
|
||||
|
||||
|
@ -19,6 +17,10 @@ public interface FileTools {
|
|||
return getFile(path).exists();
|
||||
}
|
||||
|
||||
static boolean fileNotExists(String path) {
|
||||
return !fileExists(path);
|
||||
}
|
||||
|
||||
static File getFile(String path) {
|
||||
File homeDir = application.getDir();
|
||||
File source = application.getSource();
|
||||
|
|
|
@ -6,6 +6,6 @@ spring:
|
|||
server:
|
||||
port: 10768
|
||||
help:
|
||||
default-license-name: 光云
|
||||
default-assignee-name: 藏柏
|
||||
default-expiry-date: 2111-11-11
|
||||
default-license-name: cikaros.top
|
||||
default-assignee-name: Cikaros
|
||||
default-expiry-date: 2099-12-31
|
BIN
src/main/resources/external/agent/ja-netfilter.zip
vendored
BIN
src/main/resources/external/agent/ja-netfilter.zip
vendored
Binary file not shown.
31
src/main/resources/external/certificate/license-root.key
vendored
Normal file
31
src/main/resources/external/certificate/license-root.key
vendored
Normal file
|
@ -0,0 +1,31 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
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
|
||||
7tlDFnvaRnQukqSG+VqNWg==
|
||||
-----END CERTIFICATE-----
|
|
@ -13,7 +13,8 @@
|
|||
<p>
|
||||
🇨🇳 下载 <a href="ja-netfilter" title="Download jetbra first">ja-netfilter.zip</a> , 然后配置
|
||||
你的JetBrains IDE(anything)'s <a onclick="showVmoptins()">IDE.vmoptions</a> 配置文件!<br>
|
||||
🇨🇳 当然你也可以 <a onclick="showLicenseForm()">重新定制激活授权</a> 用以自定义你的激活信息!</br>
|
||||
🇨🇳 当然你也可以 <a onclick="showLicenseForm()">重新定制激活授权</a> 用以自定义你的激活信息!<br>
|
||||
🇨🇳 也可使用Scoop进行安装:<code th:text="'scoop install ' + ${basePath}+'/scoop/ja-netfilter'"></code><br>
|
||||
<strong>🇨🇳 请注意,此页面仅由 <span
|
||||
th:text="${defaults.getDefaultLicenseName()} + '/' + ${defaults.getDefaultAssigneeName()}"></span>
|
||||
个人所有!</strong>
|
||||
|
|
Loading…
Reference in New Issue
Block a user