Merge remote-tracking branch 'origin/master'
# Conflicts: # src/main/java/com/axis/innovators/box/AxisInnovatorsBox.java
This commit is contained in:
9
.gitignore
vendored
9
.gitignore
vendored
@@ -39,4 +39,11 @@ bin/
|
|||||||
.vscode/
|
.vscode/
|
||||||
|
|
||||||
### Mac OS ###
|
### Mac OS ###
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
|
||||||
|
### logs ###
|
||||||
|
*.log
|
||||||
|
logs/
|
||||||
|
|
||||||
|
### JCEF Dlls ###
|
||||||
|
library/jcef/
|
||||||
@@ -13,6 +13,7 @@ configurations {
|
|||||||
proguardLib
|
proguardLib
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// JDK 版本检查
|
// JDK 版本检查
|
||||||
def requiredJavaVersion = 20
|
def requiredJavaVersion = 20
|
||||||
def currentJavaVersion = JavaVersion.current().majorVersion.toInteger()
|
def currentJavaVersion = JavaVersion.current().majorVersion.toInteger()
|
||||||
|
|||||||
1
gradle.properties
Normal file
1
gradle.properties
Normal file
@@ -0,0 +1 @@
|
|||||||
|
org.gradle.java.home=E:\\Softwares\\Java\\jdk-20.0.2
|
||||||
@@ -1,3 +1,3 @@
|
|||||||
#Current Loaded Language
|
#Current Loaded Language
|
||||||
#Sat Aug 16 18:11:03 CST 2025
|
#Mon Aug 18 02:11:52 CST 2025
|
||||||
loadedLanguage=system\:zh_CN
|
loadedLanguage=system\:zh_CN
|
||||||
|
|||||||
@@ -12,6 +12,8 @@ import com.axis.innovators.box.register.RegistrationSettingsItem;
|
|||||||
import com.axis.innovators.box.register.RegistrationTool;
|
import com.axis.innovators.box.register.RegistrationTool;
|
||||||
import com.axis.innovators.box.register.RegistrationTopic;
|
import com.axis.innovators.box.register.RegistrationTopic;
|
||||||
import com.axis.innovators.box.tools.*;
|
import com.axis.innovators.box.tools.*;
|
||||||
|
import com.axis.innovators.box.tools.Crypto.AESCryptoUtil;
|
||||||
|
import com.axis.innovators.box.tools.Crypto.Base64CryptoUtil;
|
||||||
import com.axis.innovators.box.util.PythonResult;
|
import com.axis.innovators.box.util.PythonResult;
|
||||||
import com.axis.innovators.box.util.Tray;
|
import com.axis.innovators.box.util.Tray;
|
||||||
import com.axis.innovators.box.verification.LoginResult;
|
import com.axis.innovators.box.verification.LoginResult;
|
||||||
@@ -44,6 +46,7 @@ import java.awt.event.WindowEvent;
|
|||||||
import java.io.*;
|
import java.io.*;
|
||||||
import java.lang.instrument.Instrumentation;
|
import java.lang.instrument.Instrumentation;
|
||||||
import java.lang.management.*;
|
import java.lang.management.*;
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.text.SimpleDateFormat;
|
import java.text.SimpleDateFormat;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -83,7 +86,7 @@ public class AxisInnovatorsBox {
|
|||||||
private final boolean isDebug;
|
private final boolean isDebug;
|
||||||
private static DebugWindow debugWindow;
|
private static DebugWindow debugWindow;
|
||||||
|
|
||||||
private LoginData loginData;
|
private static LoginData loginData;
|
||||||
|
|
||||||
public AxisInnovatorsBox(String[] args, boolean isDebug) {
|
public AxisInnovatorsBox(String[] args, boolean isDebug) {
|
||||||
this.args = args;
|
this.args = args;
|
||||||
@@ -92,12 +95,30 @@ public class AxisInnovatorsBox {
|
|||||||
organizingCrashReports(throwable instanceof Exception ?
|
organizingCrashReports(throwable instanceof Exception ?
|
||||||
(Exception) throwable : new Exception(throwable));
|
(Exception) throwable : new Exception(throwable));
|
||||||
});
|
});
|
||||||
|
// 初始化,这里为了能够在登录窗口使用主题,特意将初始化放在构造函数中
|
||||||
|
initLog4j2();
|
||||||
|
setTopic();
|
||||||
// 加载登录信息,如果没有,弹出登录弹窗,后续可以删掉默认弹出
|
// 加载登录信息,如果没有,弹出登录弹窗,后续可以删掉默认弹出
|
||||||
// TODO: login window
|
// TODO: login window should not be show when AxisInnovatorsBox initialize,
|
||||||
|
// it should be show when user click login button.
|
||||||
try {
|
try {
|
||||||
StateManager stateManager = new StateManager();
|
StateManager stateManager = new StateManager();
|
||||||
String token = stateManager.getState("loginToken");
|
String excryptedKey = "loginToken";
|
||||||
|
try {
|
||||||
|
excryptedKey = Base64CryptoUtil.base64Encode(excryptedKey);
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error("Failed to encrypt key", e);
|
||||||
|
}
|
||||||
|
String encryptedToken = stateManager.getState(excryptedKey);
|
||||||
|
String token = null;
|
||||||
|
if (encryptedToken != null && !encryptedToken.isEmpty()) {
|
||||||
|
try {
|
||||||
|
token = AESCryptoUtil.decrypt(encryptedToken);
|
||||||
|
} catch (Exception ex) {
|
||||||
|
logger.error("Token 解密失败", ex);
|
||||||
|
token = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
if (token == null || token.isEmpty()) {
|
if (token == null || token.isEmpty()) {
|
||||||
LoginResult loginResult = CasdoorLoginWindow.showLoginDialogAndGetLoginResult();
|
LoginResult loginResult = CasdoorLoginWindow.showLoginDialogAndGetLoginResult();
|
||||||
if (loginResult == null) {
|
if (loginResult == null) {
|
||||||
@@ -106,23 +127,31 @@ public class AxisInnovatorsBox {
|
|||||||
JOptionPane.INFORMATION_MESSAGE);
|
JOptionPane.INFORMATION_MESSAGE);
|
||||||
} else if (loginResult.success()) {
|
} else if (loginResult.success()) {
|
||||||
loginData = loginResult.loginData();
|
loginData = loginResult.loginData();
|
||||||
stateManager.saveState("loginToken", loginResult.token());
|
String encrypted = AESCryptoUtil.encrypt(loginResult.token());
|
||||||
logger.info("Login result: token: " + loginResult.token() + ", user: " + loginResult.user());
|
stateManager.saveState(excryptedKey, encrypted);
|
||||||
|
logger.info(
|
||||||
|
"Login result: token: " + loginResult.token() + ", user: " + loginResult.user());
|
||||||
JOptionPane.showMessageDialog(null, "登录成功", "登录",
|
JOptionPane.showMessageDialog(null, "登录成功", "登录",
|
||||||
JOptionPane.INFORMATION_MESSAGE);
|
JOptionPane.INFORMATION_MESSAGE);
|
||||||
} else {
|
} else {
|
||||||
// 登录失败,弹出错误提醒,这里只是输出登录错误信息
|
// 登录失败,弹出错误提醒,这里只是输出登录错误信息
|
||||||
logger.error("Login error: " + loginResult.message());
|
logger.error("Login error: " + loginResult.message());
|
||||||
JOptionPane.showMessageDialog(null, "登录失败: \n" + loginResult.message(), "登录失败", JOptionPane.ERROR_MESSAGE);
|
JOptionPane.showMessageDialog(null, "登录失败: \n" + loginResult.message(), "登录失败",
|
||||||
|
JOptionPane.ERROR_MESSAGE);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
CasdoorServer casdoorServer = new CasdoorServer();
|
CasdoorServer casdoorServer = new CasdoorServer();
|
||||||
User user = casdoorServer.parseJwtToken(token);
|
User user = casdoorServer.parseJwtToken(token);
|
||||||
loginData = new LoginData(token, user);
|
loginData = new LoginData(token, user);
|
||||||
}
|
}
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
logger.error("InterruptedException: Failed to load login information", e);
|
||||||
|
} catch (InvocationTargetException e) {
|
||||||
|
logger.error("InvocationTargetException: Failed to load login information", e);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
logger.error("Failed to load login information", e);
|
logger.error("Exception: Failed to load login information", e);
|
||||||
}
|
} // 添加了更详细的异常处理
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -978,8 +1007,6 @@ public class AxisInnovatorsBox {
|
|||||||
|
|
||||||
main = new AxisInnovatorsBox(args,isDebug);
|
main = new AxisInnovatorsBox(args,isDebug);
|
||||||
try {
|
try {
|
||||||
main.initLog4j2();
|
|
||||||
main.setTopic();
|
|
||||||
|
|
||||||
List<Map<String, String>> validFiles = ArgsParser.parseArgs(args);
|
List<Map<String, String>> validFiles = ArgsParser.parseArgs(args);
|
||||||
for (Map<String, String> fileInfo : validFiles) {
|
for (Map<String, String> fileInfo : validFiles) {
|
||||||
@@ -993,6 +1020,7 @@ public class AxisInnovatorsBox {
|
|||||||
}
|
}
|
||||||
|
|
||||||
main.thread = new Thread(() -> {
|
main.thread = new Thread(() -> {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// 主任务1:加载插件
|
// 主任务1:加载插件
|
||||||
logger.info("Loaded plugins Started");
|
logger.info("Loaded plugins Started");
|
||||||
|
|||||||
@@ -0,0 +1,50 @@
|
|||||||
|
package com.axis.innovators.box.tools.Crypto;
|
||||||
|
|
||||||
|
import javax.crypto.Cipher;
|
||||||
|
import javax.crypto.spec.SecretKeySpec;
|
||||||
|
|
||||||
|
import java.nio.file.*;
|
||||||
|
import java.security.SecureRandom;
|
||||||
|
import java.util.Base64;
|
||||||
|
|
||||||
|
public class AESCryptoUtil {
|
||||||
|
private static final String KEY_FILE = System.getProperty("user.home") + "/.lingqi/.axis_box_key";
|
||||||
|
|
||||||
|
// 获取密钥(Base64字符串,长度16字节)
|
||||||
|
public static byte[] getKeyBytes() throws Exception {
|
||||||
|
Path path = Paths.get(KEY_FILE);
|
||||||
|
if (Files.exists(path)) {
|
||||||
|
byte[] encrypted = Base64.getDecoder().decode(Files.readAllBytes(path));
|
||||||
|
return WindowsDPAPIUtil.unprotect(encrypted);
|
||||||
|
} else {
|
||||||
|
// 首次生成密钥
|
||||||
|
byte[] keyBytes = new byte[16];
|
||||||
|
new SecureRandom().nextBytes(keyBytes);
|
||||||
|
byte[] encrypted = WindowsDPAPIUtil.protect(keyBytes);
|
||||||
|
Files.createDirectories(path.getParent());
|
||||||
|
Files.write(path, Base64.getEncoder().encode(encrypted));
|
||||||
|
return keyBytes;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 加密
|
||||||
|
public static String encrypt(String data) throws Exception {
|
||||||
|
byte[] keyBytes = getKeyBytes();
|
||||||
|
SecretKeySpec keySpec = new SecretKeySpec(keyBytes, "AES");
|
||||||
|
Cipher cipher = Cipher.getInstance("AES");
|
||||||
|
cipher.init(Cipher.ENCRYPT_MODE, keySpec);
|
||||||
|
byte[] encrypted = cipher.doFinal(data.getBytes("UTF-8"));
|
||||||
|
return Base64.getEncoder().encodeToString(encrypted);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 解密
|
||||||
|
public static String decrypt(String encrypted) throws Exception {
|
||||||
|
byte[] keyBytes = getKeyBytes();
|
||||||
|
SecretKeySpec keySpec = new SecretKeySpec(keyBytes, "AES");
|
||||||
|
Cipher cipher = Cipher.getInstance("AES");
|
||||||
|
cipher.init(Cipher.DECRYPT_MODE, keySpec);
|
||||||
|
byte[] decoded = Base64.getDecoder().decode(encrypted);
|
||||||
|
byte[] decrypted = cipher.doFinal(decoded);
|
||||||
|
return new String(decrypted, "UTF-8");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
package com.axis.innovators.box.tools.Crypto;
|
||||||
|
|
||||||
|
import java.util.Base64;
|
||||||
|
|
||||||
|
public class Base64CryptoUtil {
|
||||||
|
|
||||||
|
public static String base64Encode(String input) {
|
||||||
|
return Base64.getEncoder().encodeToString(input.getBytes());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String base64Decode(String input) {
|
||||||
|
byte[] decodedBytes = Base64.getDecoder().decode(input);
|
||||||
|
return new String(decodedBytes);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
package com.axis.innovators.box.tools.Crypto;
|
||||||
|
|
||||||
|
import java.security.MessageDigest;
|
||||||
|
import java.util.Base64;
|
||||||
|
|
||||||
|
public class HashUtil {
|
||||||
|
public static String sha256(String input) throws Exception {
|
||||||
|
MessageDigest digest = MessageDigest.getInstance("SHA-256");
|
||||||
|
byte[] hash = digest.digest(input.getBytes("UTF-8"));
|
||||||
|
return Base64.getEncoder().encodeToString(hash); // 输出为Base64字符串
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
package com.axis.innovators.box.tools.Crypto;
|
||||||
|
|
||||||
|
import com.sun.jna.platform.win32.Crypt32Util;
|
||||||
|
|
||||||
|
public class WindowsDPAPIUtil {
|
||||||
|
// 加密
|
||||||
|
public static byte[] protect(byte[] data) {
|
||||||
|
return Crypt32Util.cryptProtectData(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 解密
|
||||||
|
public static byte[] unprotect(byte[] encrypted) {
|
||||||
|
return Crypt32Util.cryptUnprotectData(encrypted);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
package com.axis.innovators.box.tools;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
|
||||||
|
public class LoadResource {
|
||||||
|
|
||||||
|
public static String readString(String srcPath) {
|
||||||
|
try {
|
||||||
|
java.net.URL url = LoadResource.class.getResource(srcPath);
|
||||||
|
if (url == null) {
|
||||||
|
LogManager.getLogger(LoadResource.class).error("资源文件未找到: " + srcPath);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
java.nio.file.Path path = java.nio.file.Paths.get(url.toURI());
|
||||||
|
return new String(Files.readAllBytes(path), StandardCharsets.UTF_8);
|
||||||
|
} catch (Exception e) {
|
||||||
|
LogManager.getLogger(LoadResource.class).error("读取资源文件失败", e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String loadString(Class<?> clazz, String srcPath) {
|
||||||
|
try {
|
||||||
|
java.net.URL url = clazz.getResource(srcPath);
|
||||||
|
if (url == null) {
|
||||||
|
LogManager.getLogger(LoadResource.class).error("资源文件未找到: " + srcPath);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
java.nio.file.Path path = java.nio.file.Paths.get(url.toURI());
|
||||||
|
return new String(Files.readAllBytes(path), StandardCharsets.UTF_8);
|
||||||
|
} catch (Exception e) {
|
||||||
|
LogManager.getLogger(LoadResource.class).error("读取资源文件失败", e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,102 @@
|
|||||||
|
package com.axis.innovators.box.verification;
|
||||||
|
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
|
import org.casbin.casdoor.config.Config;
|
||||||
|
import org.casbin.casdoor.entity.User;
|
||||||
|
import org.casbin.casdoor.service.AuthService;
|
||||||
|
|
||||||
|
import com.axis.innovators.box.tools.LoadResource;
|
||||||
|
|
||||||
|
import config.CasdoorConfig;
|
||||||
|
|
||||||
|
public class CasdoorServer {
|
||||||
|
private static final Logger logger = LogManager.getLogger(CasdoorServer.class);
|
||||||
|
|
||||||
|
private final AuthService authService;
|
||||||
|
private final Config config;
|
||||||
|
private final String certificate;
|
||||||
|
|
||||||
|
public CasdoorServer() {
|
||||||
|
this.certificate = LoadResource.readString("/cert/casdoor_cert.pem");
|
||||||
|
this.config = new Config(
|
||||||
|
CasdoorConfig.CASDOOR_API_URL,
|
||||||
|
CasdoorConfig.CASDOOR_CLIENT_ID,
|
||||||
|
CasdoorConfig.CASDOOR_CLIENT_SECRET,
|
||||||
|
this.certificate,
|
||||||
|
CasdoorConfig.CASDOOR_ORGANIZATION_NAME,
|
||||||
|
CasdoorConfig.CASDOOR_APPLICATION_NAME);
|
||||||
|
this.authService = new AuthService(this.config);
|
||||||
|
}
|
||||||
|
|
||||||
|
public AuthService getAuthService() {
|
||||||
|
return authService;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Config getConfig() {
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getCertificate() {
|
||||||
|
return certificate;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取登录 URL
|
||||||
|
* @return 登录 URL
|
||||||
|
*/
|
||||||
|
public String getSigninUrl() {
|
||||||
|
return authService.getSigninUrl(CasdoorConfig.CASDOOR_LOGIN_REDIRECT_URI);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取注册 URL
|
||||||
|
* @return 注册 URL
|
||||||
|
*/
|
||||||
|
public String getSignupUrl() {
|
||||||
|
String redirectUrl = CasdoorConfig.CASDOOR_SIGNUP_REDIRECT_URI;
|
||||||
|
if (redirectUrl == null) {
|
||||||
|
redirectUrl = getSigninUrl(); // 如果没有设置注册回调地址,则使用登录回调地址
|
||||||
|
}
|
||||||
|
return authService.getSignupUrl(redirectUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取 OAuth Token
|
||||||
|
* @param code 登录回调的 code 参数
|
||||||
|
* @param state 登录回调的 state 参数
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public String getOAuthToken(String code, String state) {
|
||||||
|
try {
|
||||||
|
return authService.getOAuthToken(code, state);
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error("获取 OAuth Token 失败: " + e.getMessage());
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析 JWT Token
|
||||||
|
* @param token 令牌
|
||||||
|
* @return User 用户信息
|
||||||
|
*/
|
||||||
|
public User parseJwtToken(String token) {
|
||||||
|
try {
|
||||||
|
return authService.parseJwtToken(token);
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error("解析 JWT Token 失败: " + e.getMessage());
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取用户信息,同 parseJwtToken
|
||||||
|
* @param token 令牌
|
||||||
|
* @return User 用户信息
|
||||||
|
*/
|
||||||
|
public User getUserInfo(String token) {
|
||||||
|
return parseJwtToken(token);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
package com.axis.innovators.box.verification;
|
||||||
|
|
||||||
|
import org.casbin.casdoor.entity.User;
|
||||||
|
|
||||||
|
public class LoginData {
|
||||||
|
private String token;
|
||||||
|
private User user;
|
||||||
|
|
||||||
|
public LoginData(String token, User user) {
|
||||||
|
this.token = token;
|
||||||
|
this.user = user;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getToken() {
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
|
||||||
|
public User getUser() {
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
package com.axis.innovators.box.verification;
|
||||||
|
|
||||||
|
import org.casbin.casdoor.entity.User;
|
||||||
|
|
||||||
|
public class LoginResult extends Result {
|
||||||
|
|
||||||
|
public LoginResult(boolean success, String message, LoginData data) {
|
||||||
|
super(success, message, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String token() {
|
||||||
|
LoginData loginData = (LoginData)this.m_data;
|
||||||
|
return loginData != null ? loginData.getToken() : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public User user() {
|
||||||
|
LoginData loginData = (LoginData) this.m_data;
|
||||||
|
return loginData != null ? loginData.getUser() : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public LoginData loginData() {
|
||||||
|
return (LoginData) this.m_data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public LoginData data() {
|
||||||
|
return (LoginData) this.m_data;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
package com.axis.innovators.box.verification;
|
||||||
|
|
||||||
|
public class Result {
|
||||||
|
protected final boolean m_success;
|
||||||
|
protected final String m_message;
|
||||||
|
protected final Object m_data;
|
||||||
|
|
||||||
|
public Result(boolean success, String message, Object data) {
|
||||||
|
this.m_success = success;
|
||||||
|
this.m_message = message;
|
||||||
|
this.m_data = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean success() {
|
||||||
|
return m_success;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String message() {
|
||||||
|
return m_message;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object data() {
|
||||||
|
return m_data;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,89 @@
|
|||||||
|
package com.axis.innovators.box.verification.api;
|
||||||
|
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户API接口
|
||||||
|
* @author lyxyz5223
|
||||||
|
*/
|
||||||
|
public interface UserApi {
|
||||||
|
/**
|
||||||
|
* 发送验证码
|
||||||
|
* @param email 用户邮箱
|
||||||
|
* @return 发送结果
|
||||||
|
*/
|
||||||
|
CompletableFuture<UserApiResult> sendVerificationCode(String email);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 验证验证码
|
||||||
|
* @param email 用户邮箱
|
||||||
|
* @param verificationCode 验证码
|
||||||
|
* @return 验证结果
|
||||||
|
*/
|
||||||
|
CompletableFuture<UserApiResult> verifyCode(String email, String verificationCode);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检验登录token有效性,用于检验登录状态
|
||||||
|
* @param token 登录token
|
||||||
|
* @return 检验结果
|
||||||
|
*/
|
||||||
|
CompletableFuture<UserApiResult> checkTokenValidity(String token);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 申请新的token,可以定期申请新的token保证安全性
|
||||||
|
* @param oldToken 旧的登录token
|
||||||
|
* @return 申请结果
|
||||||
|
*/
|
||||||
|
CompletableFuture<UserApiResult> applyNewToken(String oldToken);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 登录接口
|
||||||
|
* @param username 用户名
|
||||||
|
* @param password 密码(加密)
|
||||||
|
* @return { token: String, expiresIn: Number } 登录结果(含token和有效期,临近到期需要手动申请新的token,每次登陆服务器将更新token有效期)
|
||||||
|
*/
|
||||||
|
CompletableFuture<UserApiResult> login(String username, String password);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 登出接口
|
||||||
|
* @param username 用户名
|
||||||
|
* @return 登出结果
|
||||||
|
*/
|
||||||
|
CompletableFuture<UserApiResult> logout(String username);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 注册接口
|
||||||
|
* @param username 用户名
|
||||||
|
* @param password 密码(加密)
|
||||||
|
* @param email 邮箱
|
||||||
|
* @return 注册结果
|
||||||
|
*/
|
||||||
|
CompletableFuture<UserApiResult> register(String username, String password, String email);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 申请重置密码接口
|
||||||
|
* @param email 用户邮箱
|
||||||
|
* @return { resetToken: String, expiresIn: Number } 重置结果(包含有效期内的重置token和有效期,24小时内有效)
|
||||||
|
*/
|
||||||
|
CompletableFuture<UserApiResult> postResetPasswordRequest(String email);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 重置密码接口
|
||||||
|
* @param email 用户邮箱
|
||||||
|
* @param resetToken 重置token(有效期24小时内)
|
||||||
|
* @param newPassword 新密码(加密)
|
||||||
|
* @return 重置结果
|
||||||
|
*/
|
||||||
|
CompletableFuture<UserApiResult> resetPassword(String email, String resetToken, String newPassword);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 修改密码接口
|
||||||
|
* @param username 用户名
|
||||||
|
* @param oldPassword 旧密码(加密)
|
||||||
|
* @param newPassword 新密码(加密)
|
||||||
|
* @return 修改结果
|
||||||
|
*/
|
||||||
|
CompletableFuture<UserApiResult> changePassword(String username, String oldPassword, String newPassword);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
package com.axis.innovators.box.verification.api;
|
||||||
|
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
|
||||||
|
public class UserApiImpl implements UserApi {
|
||||||
|
@Override
|
||||||
|
public CompletableFuture<UserApiResult> sendVerificationCode(String email) {
|
||||||
|
// Implementation here
|
||||||
|
return CompletableFuture.completedFuture(new UserApiResult(true, "Code sent", null));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CompletableFuture<UserApiResult> verifyCode(String email, String verificationCode) {
|
||||||
|
// Implementation here
|
||||||
|
return CompletableFuture.completedFuture(new UserApiResult(true, "Code verified", null));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CompletableFuture<UserApiResult> checkTokenValidity(String token) {
|
||||||
|
// Implementation here
|
||||||
|
return CompletableFuture.completedFuture(new UserApiResult(true, "Token is valid", null));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CompletableFuture<UserApiResult> applyNewToken(String oldToken) {
|
||||||
|
// Implementation here
|
||||||
|
return CompletableFuture.completedFuture(new UserApiResult(true, "New token applied", null));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CompletableFuture<UserApiResult> login(String username, String password) {
|
||||||
|
// Implementation here
|
||||||
|
return CompletableFuture.completedFuture(new UserApiResult(true, "Login successful", null));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CompletableFuture<UserApiResult> logout(String username) {
|
||||||
|
// Implementation here
|
||||||
|
return CompletableFuture.completedFuture(new UserApiResult(true, "Logout successful", null));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CompletableFuture<UserApiResult> register(String username, String password, String email) {
|
||||||
|
// Implementation here
|
||||||
|
return CompletableFuture.completedFuture(new UserApiResult(true, "Registration successful", null));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CompletableFuture<UserApiResult> postResetPasswordRequest(String email) {
|
||||||
|
// Implementation here
|
||||||
|
return CompletableFuture.completedFuture(new UserApiResult(true, "Reset password request sent", null));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CompletableFuture<UserApiResult> resetPassword(String email, String resetToken, String newPassword) {
|
||||||
|
// Implementation here
|
||||||
|
return CompletableFuture.completedFuture(new UserApiResult(true, "Password reset successful", null));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CompletableFuture<UserApiResult> changePassword(String username, String oldPassword, String newPassword) {
|
||||||
|
// Implementation here
|
||||||
|
return CompletableFuture.completedFuture(new UserApiResult(true, "Password changed successfully", null));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
package com.axis.innovators.box.verification.api;
|
||||||
|
|
||||||
|
public class UserApiResult {
|
||||||
|
private final boolean m_success;
|
||||||
|
private final String m_message;
|
||||||
|
private final Object m_data;
|
||||||
|
|
||||||
|
public UserApiResult(boolean success, String message, Object data) {
|
||||||
|
this.m_success = success;
|
||||||
|
this.m_message = message;
|
||||||
|
this.m_data = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean success() {
|
||||||
|
return m_success;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String message() {
|
||||||
|
return m_message;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object data() {
|
||||||
|
return m_data;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,299 @@
|
|||||||
|
package com.axis.innovators.box.window;
|
||||||
|
|
||||||
|
import com.axis.innovators.box.browser.CefAppManager;
|
||||||
|
import com.axis.innovators.box.tools.LoadResource;
|
||||||
|
import com.axis.innovators.box.verification.CasdoorServer;
|
||||||
|
import com.axis.innovators.box.verification.LoginData;
|
||||||
|
import com.axis.innovators.box.verification.LoginResult;
|
||||||
|
import com.axis.innovators.box.verification.Result;
|
||||||
|
import com.sun.net.httpserver.HttpServer;
|
||||||
|
import com.sun.net.httpserver.HttpExchange;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
|
import java.awt.Desktop;
|
||||||
|
import java.awt.FlowLayout;
|
||||||
|
import java.net.URI;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
|
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
|
import org.casbin.casdoor.config.Config;
|
||||||
|
import org.casbin.casdoor.entity.User;
|
||||||
|
import org.casbin.casdoor.service.AuthService;
|
||||||
|
|
||||||
|
import javax.swing.*;
|
||||||
|
import config.CasdoorConfig;
|
||||||
|
|
||||||
|
import org.cef.CefApp;
|
||||||
|
import org.cef.CefClient;
|
||||||
|
import org.cef.browser.CefBrowser;
|
||||||
|
|
||||||
|
|
||||||
|
public class CasdoorLoginWindow {
|
||||||
|
private final Logger logger = LogManager.getLogger(CasdoorLoginWindow.class);
|
||||||
|
private final CasdoorServer casdoorServer;
|
||||||
|
|
||||||
|
private CefBrowser browser; // 便于复用
|
||||||
|
private HttpServer server; // 后端处理登陆成功后的跳转
|
||||||
|
|
||||||
|
private JDialog dialog;
|
||||||
|
|
||||||
|
private LoginResult loginResult = null;
|
||||||
|
|
||||||
|
private boolean windowVisible = true;
|
||||||
|
private boolean isModal = true;
|
||||||
|
|
||||||
|
public CasdoorLoginWindow() {
|
||||||
|
casdoorServer = new CasdoorServer();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 启动本地 HTTP 服务监听 Casdoor 回调
|
||||||
|
private void startLocalCallbackServer() {
|
||||||
|
try {
|
||||||
|
server = HttpServer.create(new java.net.InetSocketAddress(CasdoorConfig.CASDOOR_WEB_SERVER_PORT), 0);
|
||||||
|
server.createContext("/casdoor/callback", this::handleCallback);
|
||||||
|
server.setExecutor(java.util.concurrent.Executors.newSingleThreadExecutor());
|
||||||
|
server.start();
|
||||||
|
} catch (IOException e) {
|
||||||
|
System.err.println("本地回调服务启动失败: " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理回调请求,自动填充 code 和 state
|
||||||
|
private void handleCallback(HttpExchange exchange) throws IOException {
|
||||||
|
String query = exchange.getRequestURI().getQuery();
|
||||||
|
AtomicReference<String> code = new AtomicReference<>("");
|
||||||
|
AtomicReference<String> state = new AtomicReference<>("");
|
||||||
|
if (query != null) {
|
||||||
|
for (String param : query.split("&")) {
|
||||||
|
String[] kv = param.split("=");
|
||||||
|
if (kv.length == 2) {
|
||||||
|
if (kv[0].equals("code"))
|
||||||
|
code.set(kv[1]);
|
||||||
|
if (kv[0].equals("state"))
|
||||||
|
state.set(kv[1]);
|
||||||
|
logger.info("Received callback with code: " + code.get() + ", state: " + state.get());
|
||||||
|
loginResult = parseUserInfo(code.get(), state.get());
|
||||||
|
String response = "Login success, please close this window.";
|
||||||
|
exchange.sendResponseHeaders(200, response.getBytes().length);
|
||||||
|
try (OutputStream os = exchange.getResponseBody()) {
|
||||||
|
os.write(response.getBytes());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initUI() {
|
||||||
|
if (browser == null) {
|
||||||
|
try {
|
||||||
|
// 初始化浏览器
|
||||||
|
CefApp cefApp = CefAppManager.getInstance();
|
||||||
|
CefClient client = cefApp.createClient();
|
||||||
|
browser = client.createBrowser(
|
||||||
|
casdoorServer.getSigninUrl(),
|
||||||
|
false,
|
||||||
|
false);
|
||||||
|
} catch (Throwable e) {
|
||||||
|
logger.error("Failed to initialize CefBrowser", e);
|
||||||
|
// 浏览器初始化失败,弹出默认浏览器替代,并提示用户
|
||||||
|
// 如果浏览器初始化失败,改为在默认浏览器打开登录页面
|
||||||
|
openCasdoorLoginPageInDefaultBrowser();
|
||||||
|
// JOptionPane.showMessageDialog(dialog,
|
||||||
|
// "浏览器初始化失败,已在默认浏览器打开登录页面,请手动完成登录。",
|
||||||
|
// "内嵌浏览器初始化失败",
|
||||||
|
// JOptionPane.WARNING_MESSAGE
|
||||||
|
// );
|
||||||
|
String message = "浏览器初始化失败,已在默认浏览器打开登录页面,请手动完成登录。\n或者手动复制下面链接在浏览器打开进行登录:\n"
|
||||||
|
+ casdoorServer.getSigninUrl();
|
||||||
|
JTextArea textArea = new JTextArea(message);
|
||||||
|
textArea.setEditable(false);
|
||||||
|
textArea.setLineWrap(true);
|
||||||
|
textArea.setWrapStyleWord(true);
|
||||||
|
textArea.setBackground(null);
|
||||||
|
textArea.setSize(600, 400);
|
||||||
|
JOptionPane.showMessageDialog(dialog, new JScrollPane(textArea), "内嵌浏览器初始化失败", JOptionPane.WARNING_MESSAGE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dialog = new JDialog();
|
||||||
|
dialog.setTitle("AXIS 认证");
|
||||||
|
dialog.setSize(500, 750);
|
||||||
|
dialog.setLocationRelativeTo(null); // 居中显示
|
||||||
|
dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
|
||||||
|
|
||||||
|
JPanel panel = new JPanel();
|
||||||
|
panel.setLayout(new BoxLayout(panel, BoxLayout.PAGE_AXIS));
|
||||||
|
|
||||||
|
if (browser != null) {
|
||||||
|
|
||||||
|
JPanel buttonPanel = new JPanel();
|
||||||
|
buttonPanel.setLayout(new FlowLayout(FlowLayout.LEFT));
|
||||||
|
|
||||||
|
JButton homeButton = new JButton("");
|
||||||
|
homeButton.setIcon(LoadIcon.loadIcon("material-symbols-home.png", 24));
|
||||||
|
homeButton.addActionListener(e -> browser.loadURL(casdoorServer.getSigninUrl()));
|
||||||
|
homeButton.setFocusPainted(false);
|
||||||
|
homeButton.setBorderPainted(false);
|
||||||
|
JButton backButton = new JButton("");
|
||||||
|
backButton.setIcon(LoadIcon.loadIcon("material-symbols-back.png", 24));
|
||||||
|
backButton.addActionListener(e -> browser.goBack());
|
||||||
|
backButton.setFocusPainted(false);
|
||||||
|
backButton.setBorderPainted(false);
|
||||||
|
JButton forwardButton = new JButton("");
|
||||||
|
forwardButton.setIcon(LoadIcon.loadIcon("material-symbols-forward.png", 24));
|
||||||
|
forwardButton.addActionListener(e -> browser.goForward());
|
||||||
|
forwardButton.setFocusPainted(false);
|
||||||
|
forwardButton.setBorderPainted(false);
|
||||||
|
JButton refreshButton = new JButton("");
|
||||||
|
refreshButton.setIcon(LoadIcon.loadIcon("material-symbols-refresh.png", 24));
|
||||||
|
refreshButton.addActionListener(e -> browser.reload());
|
||||||
|
refreshButton.setFocusPainted(false);
|
||||||
|
refreshButton.setBorderPainted(false);
|
||||||
|
// 将按钮面板添加到主panel顶部
|
||||||
|
buttonPanel.add(homeButton);
|
||||||
|
buttonPanel.add(backButton);
|
||||||
|
buttonPanel.add(forwardButton);
|
||||||
|
buttonPanel.add(refreshButton);
|
||||||
|
panel.add(buttonPanel, 0);
|
||||||
|
|
||||||
|
panel.add(browser.getUIComponent());
|
||||||
|
}
|
||||||
|
dialog.add(panel);
|
||||||
|
dialog.addWindowListener(new java.awt.event.WindowAdapter() {
|
||||||
|
@Override
|
||||||
|
public void windowClosed(java.awt.event.WindowEvent e) {
|
||||||
|
if (server != null) {
|
||||||
|
server.stop(0);
|
||||||
|
server = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void openLoginAndListen() {
|
||||||
|
if (server == null) {
|
||||||
|
startLocalCallbackServer();
|
||||||
|
}
|
||||||
|
if (browser != null) {
|
||||||
|
browser.loadURL(casdoorServer.getSigninUrl());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 在默认浏览器打开 Casdoor 登录页面
|
||||||
|
*/
|
||||||
|
private void openCasdoorLoginPageInDefaultBrowser() {
|
||||||
|
String loginUrl = casdoorServer.getSigninUrl();
|
||||||
|
try {
|
||||||
|
Desktop.getDesktop().browse(new URI(loginUrl));
|
||||||
|
} catch (Exception ex) {
|
||||||
|
JOptionPane.showMessageDialog(dialog, "无法打开浏览器: " + ex.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private LoginResult parseUserInfo(String code, String state) {
|
||||||
|
if (code.isEmpty() || state.isEmpty()) {
|
||||||
|
return new LoginResult(false, "Login failed with error: Invalid code or state.", null);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
String token = casdoorServer.getOAuthToken(code, state);
|
||||||
|
User user = casdoorServer.parseJwtToken(token);
|
||||||
|
return new LoginResult(true, "Login successful.", new LoginData(token, user));
|
||||||
|
} catch (Exception ex) {
|
||||||
|
logger.error("解析登录信息失败: " + ex.getMessage());
|
||||||
|
return new LoginResult(false, "Login failed with error: " + ex.getMessage(), null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void resetResult() {
|
||||||
|
loginResult = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 以阻塞形式显示(除非setVisible为false)窗口,同时在退出时获取返回值
|
||||||
|
* @return 返回登录结果
|
||||||
|
*/
|
||||||
|
public LoginResult exec() {
|
||||||
|
resetResult();
|
||||||
|
initUI(); // 每次显示窗口都先调用一次
|
||||||
|
openLoginAndListen();
|
||||||
|
dialog.setModal(true);
|
||||||
|
isModal = true;
|
||||||
|
dialog.setVisible(windowVisible);
|
||||||
|
return getLoginResult();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 非模态形式显示窗口(与setVisible无关)
|
||||||
|
*/
|
||||||
|
public CompletableFuture<LoginResult> show() {
|
||||||
|
resetResult();
|
||||||
|
initUI(); // 每次显示窗口都先调用一次
|
||||||
|
CompletableFuture<LoginResult> future = new CompletableFuture<>();
|
||||||
|
dialog.addWindowListener(new java.awt.event.WindowAdapter() {
|
||||||
|
@Override
|
||||||
|
public void windowClosed(java.awt.event.WindowEvent e) {
|
||||||
|
future.complete(getLoginResult());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
openLoginAndListen();
|
||||||
|
dialog.setModal(false);
|
||||||
|
isModal = false;
|
||||||
|
dialog.setVisible(true);
|
||||||
|
|
||||||
|
return future;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 窗口是否模态窗口
|
||||||
|
* @return true or false
|
||||||
|
*/
|
||||||
|
public boolean isModal() {
|
||||||
|
return isModal;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置窗口是否可见
|
||||||
|
* @param b 是否可见
|
||||||
|
*/
|
||||||
|
public void setVisible(boolean b) {
|
||||||
|
this.windowVisible = b;
|
||||||
|
dialog.setVisible(b);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取登录结果
|
||||||
|
* @return 登录结果
|
||||||
|
*/
|
||||||
|
public LoginResult getLoginResult() {
|
||||||
|
return loginResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static LoginResult showLoginDialogAndGetLoginResult() throws InterruptedException, InvocationTargetException {
|
||||||
|
AtomicReference<LoginResult> result = new AtomicReference<>();
|
||||||
|
SwingUtilities.invokeAndWait(() -> {
|
||||||
|
CasdoorLoginWindow window = new CasdoorLoginWindow();
|
||||||
|
result.set(window.exec());
|
||||||
|
});
|
||||||
|
return result.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
try {
|
||||||
|
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
|
||||||
|
Result result = showLoginDialogAndGetLoginResult();
|
||||||
|
System.out.println("Login result: " + result);
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
17
src/main/java/config/CasdoorConfig.java
Normal file
17
src/main/java/config/CasdoorConfig.java
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
package config;
|
||||||
|
|
||||||
|
public class CasdoorConfig {
|
||||||
|
// Casdoor Config Contructor
|
||||||
|
public static final String CASDOOR_API_URL = "https://casdoor.lingqi.vip";
|
||||||
|
public static final String CASDOOR_CLIENT_ID = "efb6af7c9517f19340da";
|
||||||
|
public static final String CASDOOR_CLIENT_SECRET = "7396f7e4bcda83756641179108b89356bb4d6d3b";
|
||||||
|
public static final String CASDOOR_CERTIFICATE_FILE = "";
|
||||||
|
public static final String CASDOOR_ORGANIZATION_NAME = "灵启";
|
||||||
|
public static final String CASDOOR_APPLICATION_NAME = "lingqi_box";
|
||||||
|
|
||||||
|
// Casdoor AuthService getSigninUrl
|
||||||
|
public static final String CASDOOR_LOGIN_REDIRECT_URI = "http://localhost:10487/casdoor/callback";
|
||||||
|
public static final String CASDOOR_SIGNUP_REDIRECT_URI = null; // 如果为 null 则自动跳转到登录页面,如果不需要跳转可以设置为""
|
||||||
|
public static final int CASDOOR_WEB_SERVER_PORT = 10487; // HUST
|
||||||
|
|
||||||
|
}
|
||||||
29
src/main/resources/cert/casdoor_cert.pem
Normal file
29
src/main/resources/cert/casdoor_cert.pem
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIE3TCCAsWgAwIBAgIDAeJAMA0GCSqGSIb3DQEBCwUAMCgxDjAMBgNVBAoTBWFk
|
||||||
|
bWluMRYwFAYDVQQDEw1jZXJ0LWJ1aWx0LWluMB4XDTI1MDgxNTEzNDgwMVoXDTQ1
|
||||||
|
MDgxNTEzNDgwMVowKDEOMAwGA1UEChMFYWRtaW4xFjAUBgNVBAMTDWNlcnQtYnVp
|
||||||
|
bHQtaW4wggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCm56dxzpBRDyG7
|
||||||
|
CNt3v51+ikP3dgAtgky5yUSeiQJW8eHaSJayNfIT42GCH3Nk6ZI3xR3nAZ1kaFyg
|
||||||
|
/ZiVw9QHzju3859osXoK+NMiv75G3Z0xEe2wg2gRJh0FvTDw6TKSv179C5BrxK5y
|
||||||
|
6O8caP5NAA5Kqn2OiQkIXRBxYU7i0fHrABeJpO396KtZ9oPoe9/ZWGRDTsilbyRB
|
||||||
|
8gz7JaVq9ECsgPDErmtvUhXLtNPKSfXN2UVVSpE+EET0DJv7dVlSowoaF4tNtA5e
|
||||||
|
QVTyAR559aPBnXZKD/9arTRCtU1YB2Uv3NuFELeJzchAmf/l+IPbBUfvGdaLE/kg
|
||||||
|
TB0T9hfiyRpIvjfdEpQTeaOSDVbnO165Z1AWhpxVHEHfaEDD/48aSSfAKty29CaV
|
||||||
|
wmmBA1aw90AtkyJR3BfrHExAMK4+s89P07ihe+YVUCgrrZk8J7xAQ+g3P4+FF4MI
|
||||||
|
NrhS/OgHss2e8pxoiv6rvlMXyZqFlocKuaNKCvTy0vvspj1wn6XdX8jZZnpZrKLy
|
||||||
|
Qbc1LN/HNmMe4RGjIBdlBdBHI0RIH7afLhghor+fZ/5lAa1Fzwv6TbD+QIx1wEq9
|
||||||
|
zY6lRamN4iS/AxkIEovcKJq+W0Vcn+0bovcjjmojN2MnkHWH+XCBeyEixh+fuzSi
|
||||||
|
Zjt3ydhPzU2Vb1vPsVqFQjHHR+FYbwIDAQABoxAwDjAMBgNVHRMBAf8EAjAAMA0G
|
||||||
|
CSqGSIb3DQEBCwUAA4ICAQBTFfNo5BMqwwlSimxfT2uj1xfXZ/ek0suaY9xZIVVo
|
||||||
|
XlrbIuE1j4KS1MjOCs1IbkYZW5+MO4c4OcjHh/XusQ2t2PKP7Q0atdFjYeiw987a
|
||||||
|
oE9r5am2IPzfh3DI3jC7rfz6Jrq64oK23JAXisa21xoB4CflzlpgOFX3h6UfcNhB
|
||||||
|
bTSQjdyeTZYwCeSmO53iAW9oNlffnzaYtQXqiq7pCGcC7sGZBZJnM0nNvQRYqzcU
|
||||||
|
8E8elfEwdWqc9ZJ/ah8byobZQHvraRcYDEZ3oqfjMsoycbFfJztaZV1pfJOZNRzG
|
||||||
|
lypDUCjZeK1z91K6UFws6qOGc6j06pLIXxVmpuw62yCnG3KM55O3GcUc9q3oAyTb
|
||||||
|
Iq2g3CjIiaFu8AowVyFswvOIK2pvRBBA+54eP079SLqqznHV+AE91nvVLOMn7xVu
|
||||||
|
vfV2XbxzAk3to39WilEn3KpGavpQnyQnD/nVjpEzzInAjPO6vCkWXs1ZS2sqNciz
|
||||||
|
T+r9Q3cZUMWusgNNoGfWnjchLOaOPe1zGINwAsSDXjv1YD18pfp5hEPlwSTGrulI
|
||||||
|
7fN+cGQ5Spv5VhFcOyodgmIvMnM2EcgeH4CPkQ5aiE3qAGzRzfQiPeght/Wkdd4J
|
||||||
|
+SMykEE2VmLmVD8VQf2j/EVbGQ32TTllYfQEgS/S5pfUPr5RAEljCHlCUiNcHF9c
|
||||||
|
+g==
|
||||||
|
-----END CERTIFICATE-----
|
||||||
BIN
src/main/resources/icons/material-symbols-back.png
Normal file
BIN
src/main/resources/icons/material-symbols-back.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 12 KiB |
BIN
src/main/resources/icons/material-symbols-forward.png
Normal file
BIN
src/main/resources/icons/material-symbols-forward.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 12 KiB |
BIN
src/main/resources/icons/material-symbols-home.png
Normal file
BIN
src/main/resources/icons/material-symbols-home.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 13 KiB |
BIN
src/main/resources/icons/material-symbols-refresh.png
Normal file
BIN
src/main/resources/icons/material-symbols-refresh.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 17 KiB |
0
state/toolbox.properties
Normal file
0
state/toolbox.properties
Normal file
Reference in New Issue
Block a user