mirror of
https://github.com/jie65535/gc-opencommand-plugin.git
synced 2025-06-02 17:49:12 +08:00
Impl token persistence (#19)
This commit is contained in:
parent
b70b73667d
commit
ef885af137
@ -0,0 +1,42 @@
|
||||
package com.github.jie65535.opencommand;
|
||||
|
||||
import com.github.jie65535.opencommand.model.Client;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.Vector;
|
||||
|
||||
/**
|
||||
* 插件持久化数据
|
||||
*/
|
||||
public class OpenCommandData {
|
||||
|
||||
/**
|
||||
* 连接的客户端列表
|
||||
*/
|
||||
public Vector<Client> clients = new Vector<>();
|
||||
|
||||
/**
|
||||
* 通过令牌获取客户端
|
||||
* @param token 令牌
|
||||
* @return 客户端对象,若未找到返回null
|
||||
*/
|
||||
public Client getClientByToken(String token) {
|
||||
for (var c : clients) {
|
||||
if (c.token.equals(token))
|
||||
return c;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除所有过期的客户端
|
||||
*/
|
||||
public void removeExpiredClients() {
|
||||
var now = new Date();
|
||||
clients.removeIf(client -> client.tokenExpireTime.before(now));
|
||||
}
|
||||
|
||||
public void addClient(Client client) {
|
||||
clients.add(client);
|
||||
}
|
||||
}
|
@ -19,6 +19,7 @@ package com.github.jie65535.opencommand;
|
||||
|
||||
import com.github.jie65535.opencommand.json.JsonRequest;
|
||||
import com.github.jie65535.opencommand.json.JsonResponse;
|
||||
import com.github.jie65535.opencommand.model.Client;
|
||||
import com.github.jie65535.opencommand.socket.SocketData;
|
||||
import emu.grasscutter.command.CommandMap;
|
||||
import emu.grasscutter.server.http.Router;
|
||||
@ -42,17 +43,17 @@ public final class OpenCommandHandler implements Router {
|
||||
javalin.post("/opencommand/api", OpenCommandHandler::handle);
|
||||
}
|
||||
|
||||
private static final Map<String, Integer> clients = new HashMap<>();
|
||||
private static final Map<String, Date> tokenExpireTime = new HashMap<>();
|
||||
private static final Map<String, Integer> codes = new HashMap<>();
|
||||
private static final Int2ObjectMap<Date> codeExpireTime = new Int2ObjectOpenHashMap<>();
|
||||
|
||||
public static void handle(Context context) {
|
||||
// Trigger cleanup action
|
||||
cleanupExpiredData();
|
||||
var plugin = OpenCommandPlugin.getInstance();
|
||||
var config = plugin.getConfig();
|
||||
var data = plugin.getData();
|
||||
var now = new Date();
|
||||
// Trigger cleanup action
|
||||
cleanupExpiredCodes();
|
||||
data.removeExpiredClients();
|
||||
|
||||
var req = context.bodyAsClass(JsonRequest.class);
|
||||
if (req.action.equals("sendCode")) {
|
||||
@ -74,9 +75,8 @@ public final class OpenCommandHandler implements Router {
|
||||
token = Utils.bytesToHex(Crypto.createSessionKey(32));
|
||||
int code = Utils.randomRange(1000, 9999);
|
||||
codeExpireTime.put(playerId, new Date(now.getTime() + config.codeExpirationTime_S * 1000L));
|
||||
tokenExpireTime.put(token, new Date(now.getTime() + config.tempTokenExpirationTime_S * 1000L));
|
||||
codes.put(token, code);
|
||||
clients.put(token, playerId);
|
||||
data.addClient(new Client(token, playerId, new Date(now.getTime() + config.tempTokenExpirationTime_S * 1000L)));
|
||||
player.dropMessage("[Open Command] Verification code: " + code);
|
||||
context.json(new JsonResponse(token));
|
||||
}
|
||||
@ -97,7 +97,8 @@ public final class OpenCommandHandler implements Router {
|
||||
return;
|
||||
}
|
||||
var isConsole = req.token.equals(config.consoleToken);
|
||||
if (!isConsole && !clients.containsKey(req.token)) {
|
||||
var client = data.getClientByToken(req.token);
|
||||
if (!isConsole && client == null) {
|
||||
context.json(new JsonResponse(401, "Unauthorized"));
|
||||
return;
|
||||
}
|
||||
@ -131,9 +132,10 @@ public final class OpenCommandHandler implements Router {
|
||||
if (codes.get(req.token).equals(req.data)) {
|
||||
codes.remove(req.token);
|
||||
// update token expire time
|
||||
tokenExpireTime.put(req.token, new Date(now.getTime() + config.tokenLastUseExpirationTime_H * 60L * 60L * 1000L));
|
||||
client.tokenExpireTime = new Date(now.getTime() + config.tokenLastUseExpirationTime_H * 60L * 60L * 1000L);
|
||||
context.json(new JsonResponse());
|
||||
plugin.getLogger().info(String.format("Player %d has passed the verification, ip: %s", clients.get(req.token), context.ip()));
|
||||
plugin.getLogger().info(String.format("Player %d has passed the verification, ip: %s", client.playerId, context.ip()));
|
||||
plugin.saveData();
|
||||
} else {
|
||||
context.json(new JsonResponse(400, "Verification failed"));
|
||||
}
|
||||
@ -142,9 +144,8 @@ public final class OpenCommandHandler implements Router {
|
||||
} else {
|
||||
if (req.action.equals("command")) {
|
||||
// update token expire time
|
||||
tokenExpireTime.put(req.token, new Date(now.getTime() + config.tokenLastUseExpirationTime_H * 60L * 60L * 1000L));
|
||||
var playerId = clients.get(req.token);
|
||||
var player = plugin.getServer().getPlayerByUid(playerId);
|
||||
client.tokenExpireTime = new Date(now.getTime() + config.tokenLastUseExpirationTime_H * 60L * 60L * 1000L);
|
||||
var player = plugin.getServer().getPlayerByUid(client.playerId);
|
||||
var command = req.data.toString();
|
||||
if (player == null) {
|
||||
context.json(new JsonResponse(404, "Player not found"));
|
||||
@ -171,18 +172,10 @@ public final class OpenCommandHandler implements Router {
|
||||
context.json(new JsonResponse(403, "forbidden"));
|
||||
}
|
||||
|
||||
private static void cleanupExpiredData() {
|
||||
private static void cleanupExpiredCodes() {
|
||||
var now = new Date();
|
||||
codeExpireTime.int2ObjectEntrySet().removeIf(entry -> entry.getValue().before(now));
|
||||
|
||||
var it = tokenExpireTime.entrySet().iterator();
|
||||
while (it.hasNext()) {
|
||||
var entry = it.next();
|
||||
if (entry.getValue().before(now)) {
|
||||
it.remove();
|
||||
// remove expired token
|
||||
clients.remove(entry.getKey());
|
||||
}
|
||||
}
|
||||
if (codeExpireTime.isEmpty())
|
||||
codes.clear();
|
||||
}
|
||||
}
|
||||
|
@ -19,6 +19,7 @@ package com.github.jie65535.opencommand;
|
||||
|
||||
import com.github.jie65535.opencommand.json.JsonRequest;
|
||||
import com.github.jie65535.opencommand.json.JsonResponse;
|
||||
import com.github.jie65535.opencommand.model.Client;
|
||||
import com.github.jie65535.opencommand.socket.SocketData;
|
||||
import com.github.jie65535.opencommand.socket.SocketDataWait;
|
||||
import com.github.jie65535.opencommand.socket.SocketServer;
|
||||
@ -45,17 +46,17 @@ public final class OpenCommandOnlyHttpHandler implements Router {
|
||||
javalin.post("/opencommand/api", OpenCommandOnlyHttpHandler::handle);
|
||||
}
|
||||
|
||||
private static final Map<String, Integer> clients = new HashMap<>();
|
||||
private static final Map<String, Date> tokenExpireTime = new HashMap<>();
|
||||
private static final Map<String, Integer> codes = new HashMap<>();
|
||||
private static final Int2ObjectMap<Date> codeExpireTime = new Int2ObjectOpenHashMap<>();
|
||||
|
||||
public static void handle(Context context) {
|
||||
// Trigger cleanup action
|
||||
cleanupExpiredData();
|
||||
var plugin = OpenCommandPlugin.getInstance();
|
||||
var config = plugin.getConfig();
|
||||
var data = plugin.getData();
|
||||
var now = new Date();
|
||||
// Trigger cleanup action
|
||||
cleanupExpiredCodes();
|
||||
data.removeExpiredClients();
|
||||
|
||||
var req = context.bodyAsClass(JsonRequest.class);
|
||||
if (req.action.equals("sendCode")) {
|
||||
@ -77,9 +78,8 @@ public final class OpenCommandOnlyHttpHandler implements Router {
|
||||
token = Utils.bytesToHex(Crypto.createSessionKey(32));
|
||||
int code = Utils.randomRange(1000, 9999);
|
||||
codeExpireTime.put(playerId, new Date(now.getTime() + config.codeExpirationTime_S * 1000L));
|
||||
tokenExpireTime.put(token, new Date(now.getTime() + config.tempTokenExpirationTime_S * 1000L));
|
||||
codes.put(token, code);
|
||||
clients.put(token, playerId);
|
||||
data.addClient(new Client(token, playerId, new Date(now.getTime() + config.tempTokenExpirationTime_S * 1000L)));
|
||||
Player.dropMessage(playerId, "[Open Command] Verification code: " + code);
|
||||
context.json(new JsonResponse(token));
|
||||
}
|
||||
@ -98,7 +98,8 @@ public final class OpenCommandOnlyHttpHandler implements Router {
|
||||
return;
|
||||
}
|
||||
var isConsole = req.token.equals(config.consoleToken);
|
||||
if (!isConsole && !clients.containsKey(req.token)) {
|
||||
var client = data.getClientByToken(req.token);
|
||||
if (!isConsole && client == null) {
|
||||
context.json(new JsonResponse(401, "Unauthorized"));
|
||||
return;
|
||||
}
|
||||
@ -130,12 +131,12 @@ public final class OpenCommandOnlyHttpHandler implements Router {
|
||||
};
|
||||
|
||||
SocketServer.sendPacketAndWait(server.ip, new RunConsoleCommand(req.data.toString()), wait);
|
||||
var data = wait.getData();
|
||||
if (data == null) {
|
||||
var packet = wait.getData();
|
||||
if (packet == null) {
|
||||
context.json(new JsonResponse(408, "Timeout"));
|
||||
return;
|
||||
}
|
||||
context.json(new JsonResponse(data.code, data.message, data.data));
|
||||
context.json(new JsonResponse(packet.code, packet.message, packet.data));
|
||||
return;
|
||||
} else if (req.action.equals("server")) {
|
||||
context.json(new JsonResponse(200, "Success", SocketServer.getOnlineClient()));
|
||||
@ -149,9 +150,10 @@ public final class OpenCommandOnlyHttpHandler implements Router {
|
||||
if (codes.get(req.token).equals(req.data)) {
|
||||
codes.remove(req.token);
|
||||
// update token expire time
|
||||
tokenExpireTime.put(req.token, new Date(now.getTime() + config.tokenLastUseExpirationTime_H * 60L * 60L * 1000L));
|
||||
client.tokenExpireTime = new Date(now.getTime() + config.tokenLastUseExpirationTime_H * 60L * 60L * 1000L);
|
||||
context.json(new JsonResponse());
|
||||
plugin.getLogger().info(String.format("Player %d has passed the verification, ip: %s", clients.get(req.token), context.ip()));
|
||||
plugin.getLogger().info(String.format("Player %d has passed the verification, ip: %s", client.playerId, context.ip()));
|
||||
plugin.saveData();
|
||||
} else {
|
||||
context.json(new JsonResponse(400, "Verification failed"));
|
||||
}
|
||||
@ -175,15 +177,14 @@ public final class OpenCommandOnlyHttpHandler implements Router {
|
||||
};
|
||||
|
||||
// update token expire time
|
||||
tokenExpireTime.put(req.token, new Date(now.getTime() + config.tokenLastUseExpirationTime_H * 60L * 60L * 1000L));
|
||||
var playerId = clients.get(req.token);
|
||||
client.tokenExpireTime = new Date(now.getTime() + config.tokenLastUseExpirationTime_H * 60L * 60L * 1000L);
|
||||
var command = req.data.toString();
|
||||
var player = new Player();
|
||||
player.uid = playerId;
|
||||
player.uid = client.playerId;
|
||||
player.type = PlayerEnum.RunCommand;
|
||||
player.data = command;
|
||||
|
||||
if (!SocketServer.sendUidPacketAndWait(playerId, player, socketDataWait)) {
|
||||
if (!SocketServer.sendUidPacketAndWait(client.playerId, player, socketDataWait)) {
|
||||
context.json(new JsonResponse(404, "Player Not Found."));
|
||||
return;
|
||||
}
|
||||
@ -201,18 +202,10 @@ public final class OpenCommandOnlyHttpHandler implements Router {
|
||||
context.json(new JsonResponse(403, "forbidden"));
|
||||
}
|
||||
|
||||
private static void cleanupExpiredData() {
|
||||
private static void cleanupExpiredCodes() {
|
||||
var now = new Date();
|
||||
codeExpireTime.int2ObjectEntrySet().removeIf(entry -> entry.getValue().before(now));
|
||||
|
||||
var it = tokenExpireTime.entrySet().iterator();
|
||||
while (it.hasNext()) {
|
||||
var entry = it.next();
|
||||
if (entry.getValue().before(now)) {
|
||||
it.remove();
|
||||
// remove expired token
|
||||
clients.remove(entry.getKey());
|
||||
}
|
||||
}
|
||||
if (codeExpireTime.isEmpty())
|
||||
codes.clear();
|
||||
}
|
||||
}
|
||||
|
@ -40,6 +40,8 @@ public final class OpenCommandPlugin extends Plugin {
|
||||
|
||||
private OpenCommandConfig config;
|
||||
|
||||
private OpenCommandData data;
|
||||
|
||||
private Grasscutter.ServerRunMode runMode = Grasscutter.ServerRunMode.HYBRID;
|
||||
|
||||
@Override
|
||||
@ -47,6 +49,8 @@ public final class OpenCommandPlugin extends Plugin {
|
||||
instance = this;
|
||||
// 加载配置
|
||||
loadConfig();
|
||||
// 加载数据
|
||||
loadData();
|
||||
// 启动Socket
|
||||
startSocket();
|
||||
}
|
||||
@ -77,6 +81,7 @@ public final class OpenCommandPlugin extends Plugin {
|
||||
|
||||
@Override
|
||||
public void onDisable() {
|
||||
saveData();
|
||||
getLogger().info("[OpenCommand] Disabled");
|
||||
}
|
||||
|
||||
@ -84,6 +89,10 @@ public final class OpenCommandPlugin extends Plugin {
|
||||
return config;
|
||||
}
|
||||
|
||||
public OpenCommandData getData() {
|
||||
return data;
|
||||
}
|
||||
|
||||
private void loadConfig() {
|
||||
var configFile = new File(getDataFolder(), "config.json");
|
||||
if (!configFile.exists()) {
|
||||
@ -110,6 +119,31 @@ public final class OpenCommandPlugin extends Plugin {
|
||||
}
|
||||
}
|
||||
|
||||
private void loadData() {
|
||||
var dataFile = new File(getDataFolder(), "data.json");
|
||||
if (!dataFile.exists()) {
|
||||
data = new OpenCommandData();
|
||||
saveData();
|
||||
} else {
|
||||
try {
|
||||
data = JsonUtils.loadToClass(dataFile.getAbsolutePath(), OpenCommandData.class);
|
||||
} catch (Exception exception) {
|
||||
data = new OpenCommandData();
|
||||
getLogger().error("[OpenCommand] There was an error while trying to load the data from data.json. Please make sure that there are no syntax errors. If you want to start with a default data, delete your existing data.json.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void saveData() {
|
||||
try (var file = new FileWriter(new File(getDataFolder(), "data.json"))) {
|
||||
file.write(JsonUtils.encode(data));
|
||||
} catch (IOException e) {
|
||||
getLogger().error("[OpenCommand] Unable to write to data file.");
|
||||
} catch (Exception e) {
|
||||
getLogger().error("[OpenCommand] Unable to save data file.");
|
||||
}
|
||||
}
|
||||
|
||||
private void startSocket() {
|
||||
if (runMode == Grasscutter.ServerRunMode.GAME_ONLY) {
|
||||
getLogger().info("[OpenCommand] Starting socket client...");
|
||||
|
@ -0,0 +1,16 @@
|
||||
package com.github.jie65535.opencommand.model;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
public final class Client {
|
||||
|
||||
public String token;
|
||||
public Integer playerId;
|
||||
public Date tokenExpireTime;
|
||||
|
||||
public Client(String token, Integer playerId, Date tokenExpireTime) {
|
||||
this.token = token;
|
||||
this.playerId = playerId;
|
||||
this.tokenExpireTime = tokenExpireTime;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user