feat(gui,login): 完成登录逻辑

This commit is contained in:
Hydrogen
2025-08-18 00:15:31 +08:00
parent 52231ccb22
commit ba5c07746a
21 changed files with 761 additions and 723 deletions

9
.gitignore vendored
View File

@@ -39,4 +39,11 @@ bin/
.vscode/ .vscode/
### Mac OS ### ### Mac OS ###
.DS_Store .DS_Store
### logs ###
*.log
logs/
### JCEF Dlls ###
library/jcef/

View File

@@ -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()
@@ -113,6 +114,7 @@ dependencies {
implementation 'jflac:jflac:1.3' implementation 'jflac:jflac:1.3'
implementation 'com.github.axet:TarsosDSP:2.4' implementation 'com.github.axet:TarsosDSP:2.4'
implementation 'org.json:json:20231013' implementation 'org.json:json:20231013'
implementation 'org.casbin:casdoor-java-sdk:1.37.0'
} }
configurations.all { configurations.all {

1
gradle.properties Normal file
View File

@@ -0,0 +1 @@
org.gradle.java.home=E:\\Softwares\\Java\\jdk-20.0.2

View File

@@ -14,8 +14,9 @@ import com.axis.innovators.box.register.RegistrationTopic;
import com.axis.innovators.box.tools.*; import com.axis.innovators.box.tools.*;
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.util.UserLocalInformation; import com.axis.innovators.box.verification.LoginResult;
import com.axis.innovators.box.verification.UserTags; import com.axis.innovators.box.verification.CasdoorServer;
import com.axis.innovators.box.verification.LoginData;
import com.formdev.flatlaf.themes.FlatMacDarkLaf; import com.formdev.flatlaf.themes.FlatMacDarkLaf;
import com.formdev.flatlaf.themes.FlatMacLightLaf; import com.formdev.flatlaf.themes.FlatMacLightLaf;
import com.sun.management.HotSpotDiagnosticMXBean; import com.sun.management.HotSpotDiagnosticMXBean;
@@ -31,6 +32,7 @@ import org.apache.logging.log4j.core.appender.FileAppender;
import org.apache.logging.log4j.core.appender.RollingFileAppender; import org.apache.logging.log4j.core.appender.RollingFileAppender;
import org.apache.logging.log4j.core.config.Configuration; import org.apache.logging.log4j.core.config.Configuration;
import org.api.dog.agent.VirtualMachine; import org.api.dog.agent.VirtualMachine;
import org.casbin.casdoor.entity.User;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import javax.swing.*; import javax.swing.*;
@@ -58,7 +60,8 @@ public class AxisInnovatorsBox {
private static final Logger logger = LogManager.getLogger(AxisInnovatorsBox.class); private static final Logger logger = LogManager.getLogger(AxisInnovatorsBox.class);
private static final String VERSIONS = "0.1.2"; private static final String VERSIONS = "0.1.2";
private static final String[] AUTHOR = new String[]{ private static final String[] AUTHOR = new String[]{
"tzdwindows 7" "tzdwindows 7",
"lyxyz5223",
}; };
/** 我是总任务数 **/ /** 我是总任务数 **/
@@ -77,10 +80,11 @@ public class AxisInnovatorsBox {
private final RegistrationTopic registrationTopic = new RegistrationTopic(this); private final RegistrationTopic registrationTopic = new RegistrationTopic(this);
private final List<WindowsJDialog> windowsJDialogList = new ArrayList<>(); private final List<WindowsJDialog> windowsJDialogList = new ArrayList<>();
private final StateManager stateManager = new StateManager(); private final StateManager stateManager = new StateManager();
private UserTags userTags;
private final boolean isDebug; private final boolean isDebug;
private static DebugWindow debugWindow; private static DebugWindow debugWindow;
private LoginData loginData;
public AxisInnovatorsBox(String[] args, boolean isDebug) { public AxisInnovatorsBox(String[] args, boolean isDebug) {
this.args = args; this.args = args;
this.isDebug = isDebug; this.isDebug = isDebug;
@@ -88,6 +92,37 @@ public class AxisInnovatorsBox {
organizingCrashReports(throwable instanceof Exception ? organizingCrashReports(throwable instanceof Exception ?
(Exception) throwable : new Exception(throwable)); (Exception) throwable : new Exception(throwable));
}); });
// 加载登录信息,如果没有,弹出登录弹窗,后续可以删掉默认弹出
// TODO: login window
try {
StateManager stateManager = new StateManager();
String token = stateManager.getState("loginToken");
if (token == null || token.isEmpty()) {
LoginResult loginResult = CasdoorLoginWindow.showLoginDialogAndGetLoginResult();
if (loginResult == null) {
// 用户取消登录
JOptionPane.showMessageDialog(null, "取消登录", "登录",
JOptionPane.INFORMATION_MESSAGE);
} else if (loginResult.success()) {
loginData = loginResult.loginData();
stateManager.saveState("loginToken", loginResult.token());
logger.info("Login result: token: " + loginResult.token() + ", user: " + loginResult.user());
JOptionPane.showMessageDialog(null, "登录成功", "登录",
JOptionPane.INFORMATION_MESSAGE);
} else {
// 登录失败,弹出错误提醒,这里只是输出登录错误信息
logger.error("Login error: " + loginResult.message());
JOptionPane.showMessageDialog(null, "登录失败: \n" + loginResult.message(), "登录失败", JOptionPane.ERROR_MESSAGE);
}
} else {
CasdoorServer casdoorServer = new CasdoorServer();
User user = casdoorServer.parseJwtToken(token);
loginData = new LoginData(token, user);
}
} catch (Exception e) {
logger.error("Failed to load login information", e);
}
} }
/** /**
@@ -959,14 +994,6 @@ public class AxisInnovatorsBox {
main.thread = new Thread(() -> { main.thread = new Thread(() -> {
try { try {
UserLocalInformation userLocalInformation = new UserLocalInformation(main);
main.userTags = userLocalInformation.getUserTags();
if (main.userTags == null) {
// 登录窗口
main.userTags = LoginWindow.createAndShow();
userLocalInformation.setUserTags(main.userTags);
}
// 主任务1加载插件 // 主任务1加载插件
logger.info("Loaded plugins Started"); logger.info("Loaded plugins Started");
main.progressBarManager.updateMainProgress(++main.completedTasks); main.progressBarManager.updateMainProgress(++main.completedTasks);
@@ -1092,13 +1119,6 @@ public class AxisInnovatorsBox {
return AUTHOR; return AUTHOR;
} }
/**
* 获取用户标签
* @return 用户标签
*/
public UserTags getUserTags() {
return userTags;
}
/** /**
* 获取状态管理器 * 获取状态管理器

View File

@@ -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;
}
}
}

View File

@@ -1,47 +0,0 @@
package com.axis.innovators.box.util;
import com.axis.innovators.box.AxisInnovatorsBox;
import com.axis.innovators.box.verification.OnlineVerification;
import com.axis.innovators.box.verification.UserTags;
import com.axis.innovators.box.verification.VerificationService;
/**
* 用于存储用户信息
* @author tzdwindows 7
*/
public class UserLocalInformation {
private final AxisInnovatorsBox main;
public UserLocalInformation(AxisInnovatorsBox main){
this.main = main;
}
/**
* 设置用户信息
* @param userTags 用户信息
*/
public void setUserTags(UserTags userTags){
OnlineVerification onlineVerification = userTags.getUser();
main.getStateManager().saveState("password", onlineVerification.password);
main.getStateManager().saveState("verification", onlineVerification.onlineVerification);
}
/**
* 获取用户信息
* @return 用户信息
*/
public UserTags getUserTags(){
String verification = main.getStateManager().getState("verification");
String password = main.getStateManager().getState("password");
if (verification == null || password == null){
return null;
}
OnlineVerification onlineVerification = OnlineVerification.validateLogin(
verification,
password);
if (onlineVerification == null){
return null;
}
return VerificationService.determineUserType(onlineVerification);
}
}

View File

@@ -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);
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -1,33 +0,0 @@
package com.axis.innovators.box.verification;
/**
* 在线验证用户身份
* 用于报错用户的验证信息
* @author tzdwindows 7
*/
public class OnlineVerification {
public String onlineVerification;
public String password;
/* 我是错误信息,要返回错误请修改我 */
public static String errorMessage = "用户不存在";
/**
* 验证登录
* @param identifier 账号
* @param password 密码
*/
OnlineVerification(String identifier, String password){
this.onlineVerification = identifier;
this.password = password;
}
/**
* 验证登录
* @param identifier 账号
* @param password 密码
* @return 验证结果,如果返回null则表示验证失败使用errorMessage获取验证失败的原因
*/
public static OnlineVerification validateLogin(String identifier, String password){
return new OnlineVerification(identifier, password);
}
}

View File

@@ -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;
}
}

View File

@@ -1,46 +0,0 @@
package com.axis.innovators.box.verification;
/**
* 用户标签组
* @author tzdwindows 7
*/
public enum UserTags {
/**
* 没有登录的标签
*/
None,
/**
* 普通用户标签
*/
RegularUsers,
/**
* 管理员标签
*/
AdminUsers,
/**
* VIP用户标签
*/
VipUsers,
/**
* SVip用户标签
*/
SVipUsers,
/**
* 企业用户标签
*/
EnterpriseUsers;
private OnlineVerification onlineVerification;
/**
* 设置用户组信息
* @param onlineVerification 用户验证结果信息
*/
void setUser(OnlineVerification onlineVerification) {
this.onlineVerification = onlineVerification;
}
public OnlineVerification getUser() {
return onlineVerification;
}
}

View File

@@ -1,38 +0,0 @@
package com.axis.innovators.box.verification;
/**
* @author tzdwindows 7
*/
public class VerificationService {
/**
* 确定用户类型
* @param identifier 用户
* @return 用户类型
*/
public static UserTags determineUserType(OnlineVerification identifier) {
UserTags userTags = UserTags.RegularUsers;
userTags.setUser(identifier);
return userTags;
}
/**
* 发送密码重置链接给用户
* @param text
* @return
*/
public static boolean sendPasswordReset(String text) {
return true;
}
/**
* 注册用户
* @param text
* @param text1
* @param pwd
* @return
*/
public static boolean registerUser(String text, String text1, String pwd) {
return true;
}
}

View File

@@ -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);
}

View File

@@ -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));
}
}

View File

@@ -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;
}
}

View File

@@ -0,0 +1,268 @@
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() {
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));
JPanel buttonPanel = new JPanel();
buttonPanel.setLayout(new FlowLayout(FlowLayout.LEFT));
JButton backButton = new JButton("");
backButton.setIcon(LoadIcon.loadIcon("back.png", 24));
backButton.addActionListener(e -> browser.goBack());
backButton.setFocusPainted(false);
backButton.setBorderPainted(false);
JButton forwardButton = new JButton("");
forwardButton.setIcon(LoadIcon.loadIcon("forward.png", 24));
forwardButton.addActionListener(e -> browser.goForward());
forwardButton.setFocusPainted(false);
forwardButton.setBorderPainted(false);
JButton refreshButton = new JButton("");
refreshButton.setIcon(LoadIcon.loadIcon("refresh.png", 24));
refreshButton.addActionListener(e -> browser.reload());
refreshButton.setFocusPainted(false);
refreshButton.setBorderPainted(false);
// 将按钮面板添加到主panel顶部
buttonPanel.add(backButton);
buttonPanel.add(forwardButton);
buttonPanel.add(refreshButton);
panel.add(buttonPanel, 0);
if (browser == null) {
// 初始化浏览器
CefApp cefApp = CefAppManager.getInstance();
CefClient client = cefApp.createClient();
browser = client.createBrowser(
casdoorServer.getSigninUrl(),
false,
false);
}
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();
}
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();
}
}
}

View File

@@ -1,539 +0,0 @@
package com.axis.innovators.box.window;
import com.axis.innovators.box.verification.OnlineVerification;
import com.axis.innovators.box.verification.UserTags;
import com.axis.innovators.box.verification.VerificationService;
import com.formdev.flatlaf.FlatDarculaLaf;
import javax.swing.*;
import javax.swing.border.LineBorder;
import javax.swing.text.JTextComponent;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.lang.reflect.InvocationTargetException;
import java.util.concurrent.atomic.AtomicReference;
/**
* 重构后的现代化单窗口登录/注册/找回密码界面(带平滑切换动画)
* 保留原有验证逻辑接口调用OnlineVerification / VerificationService
*
* 说明:
* - 单窗口JDialog内使用滑动动画切换视图仿微软登录体验
* - 所有子界面(登录/注册/找回密码)都在同一容器中切换,不再弹新窗口。
* - 按钮与输入框固定宽度,避免被挤压变形。
*
* 注意:需要 flatlaf 依赖以呈现更现代的外观。
*/
public class LoginWindow {
private static final AtomicReference<UserTags> loginResult = new AtomicReference<>(UserTags.None);
private final JDialog dialog;
private final JLayeredPane layeredPane;
private final int DIALOG_WIDTH = 460;
private final int DIALOG_HEIGHT = 560;
// 登录面板中的控件需要在类域以便访问
private JTextField loginEmailField;
private JPasswordField loginPasswordField;
public static UserTags createAndShow() throws InterruptedException, InvocationTargetException {
AtomicReference<UserTags> result = new AtomicReference<>(UserTags.None);
SwingUtilities.invokeAndWait(() -> {
LoginWindow window = new LoginWindow();
window.dialog.setVisible(true);
result.set(loginResult.get());
if (result.get() == UserTags.None) {
System.exit(0);
}
});
return result.get();
}
public LoginWindow() {
setupLookAndFeel();
dialog = new JDialog((Frame) null, "AXIS 安全认证", true);
dialog.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
dialog.setSize(DIALOG_WIDTH, DIALOG_HEIGHT);
dialog.setResizable(false);
dialog.setLocationRelativeTo(null);
// 根容器:深色背景并居中卡片
JPanel root = new JPanel(new GridBagLayout());
root.setBackground(new Color(0x202225));
root.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20));
dialog.setContentPane(root);
// 卡片容器(居中)
JPanel cardWrapper = new JPanel(null) {
@Override
public Dimension getPreferredSize() {
return new Dimension(DIALOG_WIDTH - 40, DIALOG_HEIGHT - 40);
}
};
cardWrapper.setOpaque(false);
cardWrapper.setPreferredSize(new Dimension(DIALOG_WIDTH - 40, DIALOG_HEIGHT - 40));
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridx = 0;
gbc.gridy = 0;
root.add(cardWrapper, gbc);
// 分层面板用于动画
layeredPane = new JLayeredPane();
layeredPane.setBounds(0, 0, DIALOG_WIDTH - 40, DIALOG_HEIGHT - 40);
cardWrapper.add(layeredPane);
// 卡片背景(圆角)
JPanel backgroundCard = new JPanel();
backgroundCard.setBackground(new Color(0x2A2E31));
backgroundCard.setBorder(new RoundBorder(16, new Color(0x3A3F42)));
backgroundCard.setBounds(0, 0, layeredPane.getWidth(), layeredPane.getHeight());
backgroundCard.setLayout(null);
layeredPane.add(backgroundCard, Integer.valueOf(0));
// 创建三个面板宽度等于容器宽度初始将登录面板放置在0位置
JPanel loginPanel = buildLoginPanel();
JPanel registerPanel = buildRegisterPanel();
JPanel forgotPanel = buildForgotPanel();
int w = layeredPane.getWidth();
int h = layeredPane.getHeight();
loginPanel.setBounds(0, 0, w, h);
registerPanel.setBounds(w, 0, w, h); // 右侧预置
forgotPanel.setBounds(w * 2, 0, w, h);
layeredPane.add(loginPanel, Integer.valueOf(1));
layeredPane.add(registerPanel, Integer.valueOf(1));
layeredPane.add(forgotPanel, Integer.valueOf(1));
dialog.pack();
// ensure layered sizes match after pack
SwingUtilities.invokeLater(() -> {
layeredPane.setBounds(0, 0, cardWrapper.getWidth(), cardWrapper.getHeight());
backgroundCard.setBounds(0, 0, layeredPane.getWidth(), layeredPane.getHeight());
int nw = layeredPane.getWidth(), nh = layeredPane.getHeight();
loginPanel.setBounds(0, 0, nw, nh);
registerPanel.setBounds(nw, 0, nw, nh);
forgotPanel.setBounds(nw * 2, 0, nw, nh);
});
}
// 切换动画direction = 1 表示向左滑动进入下一页(当前 -> 右侧),-1 表示向右滑动进入上一页
private void slideTo(int targetIndex) {
Component[] comps = layeredPane.getComponents();
// 每个面板宽度
int w = layeredPane.getWidth();
// 当前最左边的x位置找到最左的那个主要面板
// 我们采用简单方式:目标面板应该位于 x = targetIndex * w 0,1,2
int targetX = -targetIndex * w; // 我们会将所有面板整体平移,使目标显示在 x=0
// 获取当前 offset (使用第一个面板的 x 来代表整体偏移)
int startOffset = 0;
// find current offset by checking bounds of first panel (assume index 0 is login)
if (comps.length > 0) {
startOffset = comps[0].getX();
}
int start = startOffset;
int end = targetX;
int duration = 300; // ms
int fps = 60;
int delay = 1000 / fps;
int steps = Math.max(1, duration / delay);
final int[] step = {0};
Timer timer = new Timer(delay, null);
timer.addActionListener((ActionEvent e) -> {
step[0]++;
double t = (double) step[0] / steps;
// ease in-out cubic
double tt = t < 0.5 ? 4 * t * t * t : 1 - Math.pow(-2 * t + 2, 3) / 2;
int cur = (int) Math.round(start + (end - start) * tt);
// 平移所有在 layeredPane 中(除背景)组件
for (Component c : layeredPane.getComponents()) {
if (c instanceof JPanel && c.isVisible()) {
// 计算原始索引根据名字
String name = c.getName();
// 我们之前把panel放在 x = idx * w ; 现在把它设置为 idx*w + cur
int idx = 0;
if ("login".equals(name)) idx = 0;
else if ("register".equals(name)) idx = 1;
else if ("forgot".equals(name)) idx = 2;
c.setLocation(idx * w + cur, 0);
}
}
layeredPane.repaint();
if (step[0] >= steps) {
timer.stop();
}
});
timer.setInitialDelay(0);
timer.start();
}
private JPanel buildLoginPanel() {
JPanel p = new JPanel(null);
p.setOpaque(false);
p.setName("login");
int w = DIALOG_WIDTH - 40;
int h = DIALOG_HEIGHT - 40;
// 标题区
JLabel appTitle = new JLabel("AXIS");
appTitle.setFont(new Font("微软雅黑", Font.BOLD, 28));
appTitle.setForeground(Color.WHITE);
appTitle.setBounds(28, 20, w - 56, 36);
JLabel subtitle = new JLabel("安全认证 — 请登录以继续");
subtitle.setFont(new Font("微软雅黑", Font.PLAIN, 12));
subtitle.setForeground(new Color(0xA7AEB5));
subtitle.setBounds(28, 56, w - 56, 18);
p.add(appTitle);
p.add(subtitle);
// 卡片内控件起始 y
int startY = 96;
int fieldW = Math.min(360, w - 56);
int fieldX = (w - fieldW) / 2;
// Email
JLabel emailLabel = new JLabel("账号");
emailLabel.setForeground(new Color(0x99A0A7));
emailLabel.setFont(new Font("微软雅黑", Font.PLAIN, 13));
emailLabel.setBounds(fieldX, startY, fieldW, 18);
loginEmailField = new JTextField();
styleInput(loginEmailField);
loginEmailField.setBounds(fieldX, startY + 22, fieldW, 44);
loginEmailField.putClientProperty("JTextField.placeholderText", "邮箱或手机号");
// Password
JLabel pwdLabel = new JLabel("密码");
pwdLabel.setForeground(new Color(0x99A0A7));
pwdLabel.setFont(new Font("微软雅黑", Font.PLAIN, 13));
pwdLabel.setBounds(fieldX, startY + 22 + 44 + 12, fieldW, 18);
loginPasswordField = new JPasswordField();
styleInput(loginPasswordField);
loginPasswordField.setBounds(fieldX, startY + 22 + 44 + 12 + 20, fieldW - 48, 44);
loginPasswordField.putClientProperty("JTextField.placeholderText", "请输入密码");
// eye toggle
JToggleButton eyeBtn = new JToggleButton("显示");
eyeBtn.setFont(new Font("微软雅黑", Font.PLAIN, 12));
eyeBtn.setFocusable(false);
eyeBtn.setBorderPainted(false);
eyeBtn.setContentAreaFilled(true);
eyeBtn.setBackground(new Color(0x39424A));
eyeBtn.setForeground(Color.WHITE);
eyeBtn.setBounds(fieldX + fieldW - 44, startY + 22 + 44 + 12 + 20, 44, 44);
eyeBtn.addActionListener(e -> {
if (eyeBtn.isSelected()) loginPasswordField.setEchoChar((char) 0);
else loginPasswordField.setEchoChar('•');
});
// 登录按钮(占满宽度)
JButton loginBtn = new JButton("立即登录");
stylePrimaryButton(loginBtn);
loginBtn.setBounds(fieldX, startY + 22 + 44 + 12 + 20 + 44 + 22, fieldW, 48);
loginBtn.addActionListener(e -> doLogin());
// 链接区域(注册 / 忘记密码) — 点击切换到对应面板
JButton toRegister = createTextLink("注册账号");
toRegister.setBounds(fieldX, startY + 22 + 44 + 12 + 20 + 44 + 22 + 60, 120, 20);
toRegister.addActionListener(e -> slideTo(1));
JButton toForgot = createTextLink("忘记密码");
toForgot.setBounds(fieldX + fieldW - 120, startY + 22 + 44 + 12 + 20 + 44 + 22 + 60, 120, 20);
toForgot.addActionListener(e -> slideTo(2));
// footer
JLabel footer = new JLabel("使用你的 AXIS 账户进行登录。");
footer.setForeground(new Color(0x8F969C));
footer.setFont(new Font("微软雅黑", Font.PLAIN, 11));
footer.setBounds(fieldX, h - 44, fieldW, 18);
p.add(emailLabel);
p.add(loginEmailField);
p.add(pwdLabel);
p.add(loginPasswordField);
p.add(eyeBtn);
p.add(loginBtn);
p.add(toRegister);
p.add(toForgot);
p.add(footer);
return p;
}
private JPanel buildRegisterPanel() {
JPanel p = new JPanel(null);
p.setOpaque(false);
p.setName("register");
int w = DIALOG_WIDTH - 40;
int h = DIALOG_HEIGHT - 40;
JLabel title = new JLabel("创建账号");
title.setFont(new Font("微软雅黑", Font.BOLD, 24));
title.setForeground(Color.WHITE);
title.setBounds(28, 20, w - 56, 36);
JLabel desc = new JLabel("快速创建你的 AXIS 帐号");
desc.setFont(new Font("微软雅黑", Font.PLAIN, 12));
desc.setForeground(new Color(0xA7AEB5));
desc.setBounds(28, 56, w - 56, 18);
p.add(title);
p.add(desc);
int startY = 96;
int fieldW = Math.min(360, w - 56);
int fieldX = (w - fieldW) / 2;
// 用户名
JLabel nameLabel = new JLabel("用户名");
nameLabel.setForeground(new Color(0x99A0A7));
nameLabel.setFont(new Font("微软雅黑", Font.PLAIN, 13));
nameLabel.setBounds(fieldX, startY, fieldW, 18);
JTextField nameField = new JTextField();
styleInput(nameField);
nameField.setBounds(fieldX, startY + 22, fieldW, 44);
// 邮箱
JLabel emailLabel = new JLabel("邮箱");
emailLabel.setForeground(new Color(0x99A0A7));
emailLabel.setFont(new Font("微软雅黑", Font.PLAIN, 13));
emailLabel.setBounds(fieldX, startY + 22 + 44 + 12, fieldW, 18);
JTextField emailField = new JTextField();
styleInput(emailField);
emailField.setBounds(fieldX, startY + 22 + 44 + 12 + 20, fieldW, 44);
// 密码
JLabel pwdLabel = new JLabel("密码");
pwdLabel.setForeground(new Color(0x99A0A7));
pwdLabel.setFont(new Font("微软雅黑", Font.PLAIN, 13));
pwdLabel.setBounds(fieldX, startY + 22 + 44 + 12 + 20 + 44 + 12, fieldW, 18);
JPasswordField pwdField = new JPasswordField();
styleInput(pwdField);
pwdField.setBounds(fieldX, startY + 22 + 44 + 12 + 20 + 44 + 12 + 20, fieldW, 44);
// 确认密码
JLabel confirmLabel = new JLabel("确认密码");
confirmLabel.setForeground(new Color(0x99A0A7));
confirmLabel.setFont(new Font("微软雅黑", Font.PLAIN, 13));
confirmLabel.setBounds(fieldX, startY + 22 + 44 + 12 + 20 + 44 + 12 + 20 + 44 + 12, fieldW, 18);
JPasswordField confirmField = new JPasswordField();
styleInput(confirmField);
confirmField.setBounds(fieldX, startY + 22 + 44 + 12 + 20 + 44 + 12 + 20 + 44 + 12 + 20, fieldW, 44);
// 注册按钮
JButton regBtn = new JButton("创建账号");
stylePrimaryButton(regBtn);
regBtn.setBounds(fieldX, startY + 22 + 44 + 12 + 20 + 44 + 12 + 20 + 44 + 12 + 20 + 44 + 18, fieldW, 48);
regBtn.addActionListener(e -> {
String name = nameField.getText().trim();
String email = emailField.getText().trim();
String pwd = new String(pwdField.getPassword());
String confirm = new String(confirmField.getPassword());
if (name.isEmpty() || email.isEmpty() || pwd.isEmpty() || confirm.isEmpty()) {
JOptionPane.showMessageDialog(dialog, "请完整填写注册信息", "注册错误", JOptionPane.ERROR_MESSAGE);
return;
}
if (!pwd.equals(confirm)) {
JOptionPane.showMessageDialog(dialog, "两次输入的密码不一致", "注册错误", JOptionPane.ERROR_MESSAGE);
return;
}
boolean success = VerificationService.registerUser(name, email, pwd);
if (success) {
JOptionPane.showMessageDialog(dialog, "注册成功,请登录", "注册成功", JOptionPane.INFORMATION_MESSAGE);
slideTo(0); // 回到登录页面
} else {
JOptionPane.showMessageDialog(dialog, "注册失败,请检查信息", "注册错误", JOptionPane.ERROR_MESSAGE);
}
});
// 返回登录链接
JButton back = createTextLink("返回登录");
back.setBounds(fieldX, regBtn.getY() + regBtn.getHeight() + 12, 120, 20);
back.addActionListener(e -> slideTo(0));
p.add(nameLabel);
p.add(nameField);
p.add(emailLabel);
p.add(emailField);
p.add(pwdLabel);
p.add(pwdField);
p.add(confirmLabel);
p.add(confirmField);
p.add(regBtn);
p.add(back);
return p;
}
private JPanel buildForgotPanel() {
JPanel p = new JPanel(null);
p.setOpaque(false);
p.setName("forgot");
int w = DIALOG_WIDTH - 40;
int h = DIALOG_HEIGHT - 40;
JLabel title = new JLabel("找回密码");
title.setFont(new Font("微软雅黑", Font.BOLD, 24));
title.setForeground(Color.WHITE);
title.setBounds(28, 20, w - 56, 36);
JLabel desc = new JLabel("通过注册邮箱重置密码");
desc.setFont(new Font("微软雅黑", Font.PLAIN, 12));
desc.setForeground(new Color(0xA7AEB5));
desc.setBounds(28, 56, w - 56, 18);
p.add(title);
p.add(desc);
int startY = 110;
int fieldW = Math.min(360, w - 56);
int fieldX = (w - fieldW) / 2;
JLabel emailLabel = new JLabel("注册邮箱");
emailLabel.setForeground(new Color(0x99A0A7));
emailLabel.setFont(new Font("微软雅黑", Font.PLAIN, 13));
emailLabel.setBounds(fieldX, startY, fieldW, 18);
JTextField emailField = new JTextField();
styleInput(emailField);
emailField.setBounds(fieldX, startY + 22, fieldW, 44);
JButton sendBtn = new JButton("发送重置邮件");
stylePrimaryButton(sendBtn);
sendBtn.setBounds(fieldX, startY + 22 + 44 + 22, fieldW, 48);
sendBtn.addActionListener(e -> {
String email = emailField.getText().trim();
if (email.isEmpty()) {
JOptionPane.showMessageDialog(dialog, "请输入注册邮箱", "错误", JOptionPane.ERROR_MESSAGE);
return;
}
if (VerificationService.sendPasswordReset(email)) {
JOptionPane.showMessageDialog(dialog, "重置邮件已发送,请查收", "成功", JOptionPane.INFORMATION_MESSAGE);
slideTo(0);
} else {
JOptionPane.showMessageDialog(dialog, "发送失败或邮箱未注册", "失败", JOptionPane.ERROR_MESSAGE);
}
});
JButton back = createTextLink("返回登录");
back.setBounds(fieldX, sendBtn.getY() + sendBtn.getHeight() + 12, 120, 20);
back.addActionListener(e -> slideTo(0));
p.add(emailLabel);
p.add(emailField);
p.add(sendBtn);
p.add(back);
return p;
}
private void doLogin() {
String email = loginEmailField.getText().trim();
String password = new String(loginPasswordField.getPassword()).trim();
if (email.isEmpty() || password.isEmpty()) {
JOptionPane.showMessageDialog(dialog, "请输入完整的登录信息", "验证错误", JOptionPane.ERROR_MESSAGE);
return;
}
OnlineVerification onlineVerification = OnlineVerification.validateLogin(email, password);
if (onlineVerification == null) {
String err = OnlineVerification.errorMessage != null && !OnlineVerification.errorMessage.trim().isEmpty()
? OnlineVerification.errorMessage
: "验证失败,请重试";
JOptionPane.showMessageDialog(dialog, err, "验证错误", JOptionPane.ERROR_MESSAGE);
return;
}
loginResult.set(VerificationService.determineUserType(onlineVerification));
dialog.dispose();
}
// 通用输入框样式
private void styleInput(JComponent comp) {
comp.setFont(new Font("微软雅黑", Font.PLAIN, 14));
comp.setBackground(new Color(0x2F3336));
comp.setForeground(new Color(0xE8ECEF));
comp.setBorder(BorderFactory.createCompoundBorder(
new RoundBorder(8, new Color(0x3A3F42)),
BorderFactory.createEmptyBorder(10, 12, 10, 12)
));
if (comp instanceof JTextComponent) ((JTextComponent) comp).setCaretColor(new Color(0x9AA0A6));
}
// 主要操作按钮样式(主色)
private void stylePrimaryButton(AbstractButton b) {
b.setFont(new Font("微软雅黑", Font.BOLD, 14));
b.setForeground(Color.WHITE);
b.setBackground(new Color(0x2B79D0));
b.setBorderPainted(false);
b.setFocusPainted(false);
b.setOpaque(true);
b.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
}
private JButton createTextLink(String text) {
JButton btn = new JButton(text);
btn.setFont(new Font("微软雅黑", Font.PLAIN, 12));
btn.setForeground(new Color(0x79A6FF));
btn.setBorderPainted(false);
btn.setContentAreaFilled(false);
btn.setFocusPainted(false);
btn.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
return btn;
}
private void setupLookAndFeel() {
try {
UIManager.setLookAndFeel(new FlatDarculaLaf());
UIManager.put("Component.arc", 12);
UIManager.put("TextComponent.arc", 12);
UIManager.put("Button.arc", 10);
UIManager.put("Panel.background", new Color(0x202225));
UIManager.put("TextComponent.background", new Color(0x2F3336));
UIManager.put("TextComponent.foreground", new Color(0xE8ECEF));
} catch (UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
}
// 圆角边框
private static class RoundBorder extends LineBorder {
private final int radius;
public RoundBorder(int radius, Color color) {
super(color, 1);
this.radius = radius;
}
@Override
public void paintBorder(Component c, Graphics g, int x, int y, int width, int height) {
Graphics2D g2 = (Graphics2D) g.create();
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2.setColor(lineColor);
g2.drawRoundRect(x, y, width - 1, height - 1, radius, radius);
g2.dispose();
}
}
}

View 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
}

View 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-----

0
state/toolbox.properties Normal file
View File