[FEATURE][EXPERIMENTAL] Работа с сессиями

This commit is contained in:
Gravit 2020-10-26 23:17:05 +07:00
parent 3a80a74912
commit 18b23b195a
No known key found for this signature in database
GPG key ID: 98A079490768CCE5
8 changed files with 98 additions and 24 deletions

View file

@ -139,7 +139,7 @@ public LaunchServer(LaunchServerDirectories directories, LaunchServerEnv env, La
// build hooks, anti-brutforce and other // build hooks, anti-brutforce and other
proguardConf = new ProguardConf(this); proguardConf = new ProguardConf(this);
sessionManager = new SessionManager(); sessionManager = new SessionManager(this);
mirrorManager = new MirrorManager(); mirrorManager = new MirrorManager();
reconfigurableManager = new ReconfigurableManager(); reconfigurableManager = new ReconfigurableManager();
authHookManager = new AuthHookManager(); authHookManager = new AuthHookManager();

View file

@ -0,0 +1,4 @@
package pro.gravit.launchserver.auth;
public interface RequiredDAO {
}

View file

@ -3,11 +3,12 @@
import pro.gravit.launcher.request.auth.AuthRequest; import pro.gravit.launcher.request.auth.AuthRequest;
import pro.gravit.launcher.request.auth.password.AuthPlainPassword; import pro.gravit.launcher.request.auth.password.AuthPlainPassword;
import pro.gravit.launchserver.auth.AuthException; import pro.gravit.launchserver.auth.AuthException;
import pro.gravit.launchserver.auth.RequiredDAO;
import pro.gravit.launchserver.dao.User; import pro.gravit.launchserver.dao.User;
import pro.gravit.launchserver.manangers.hook.AuthHookManager; import pro.gravit.launchserver.manangers.hook.AuthHookManager;
import pro.gravit.utils.helper.SecurityHelper; import pro.gravit.utils.helper.SecurityHelper;
public class HibernateAuthProvider extends AuthProvider { public class HibernateAuthProvider extends AuthProvider implements RequiredDAO {
public boolean autoReg; public boolean autoReg;
@Override @Override

View file

@ -1,63 +1,101 @@
package pro.gravit.launchserver.manangers; package pro.gravit.launchserver.manangers;
import pro.gravit.launcher.Launcher;
import pro.gravit.launcher.NeedGarbageCollection; import pro.gravit.launcher.NeedGarbageCollection;
import pro.gravit.launchserver.LaunchServer;
import pro.gravit.launchserver.auth.RequiredDAO;
import pro.gravit.launchserver.socket.Client; import pro.gravit.launchserver.socket.Client;
import pro.gravit.utils.helper.LogHelper;
import java.util.*; import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function; import java.util.function.Function;
import java.util.stream.Collectors; import java.util.stream.Collectors;
public class SessionManager implements NeedGarbageCollection { public class SessionManager implements NeedGarbageCollection {
public static final long SESSION_TIMEOUT = 3 * 60 * 60 * 1000; // 3 часа public static final long SESSION_TIMEOUT = 3 * 60 * 60 * 1000; // 3 часа
private final Map<UUID, Client> clientSet = new HashMap<>(128); private final Map<UUID, Entry> clientSet = new ConcurrentHashMap<>(128);
private final LaunchServer server;
public SessionManager(LaunchServer server) {
this.server = server;
}
public boolean addClient(Client client) { public boolean addClient(Client client) {
clientSet.put(client.session, client); if(client == null) return false;
clientSet.put(client.session, new Entry(compressClient(client)));
return true; return true;
} }
private String compressClient(Client client) {
return Launcher.gsonManager.gson.toJson(client); //Compress using later
}
private Client decompressClient(String client) {
return Launcher.gsonManager.gson.fromJson(client, Client.class); //Compress using later
}
private Client restoreFromString(String data) {
Client result = decompressClient(data);
result.updateAuth(server);
if(result.auth != null && (result.username != null)) {
if(result.auth.handler instanceof RequiredDAO || result.auth.provider instanceof RequiredDAO || result.auth.textureProvider instanceof RequiredDAO) {
result.daoObject = server.config.dao.userDAO.findByUsername(result.username);
}
}
return result;
}
@Override @Override
public void garbageCollection() { public void garbageCollection() {
long time = System.currentTimeMillis(); long time = System.currentTimeMillis();
clientSet.entrySet().removeIf(entry -> { clientSet.entrySet().removeIf(entry -> {
Client c = entry.getValue(); long timestamp = entry.getValue().timestamp;
return (c.timestamp + SESSION_TIMEOUT < time); return (timestamp + SESSION_TIMEOUT < time);
}); });
} }
public Client getClient(UUID session) { public Client getClient(UUID session) {
return clientSet.get(session); Entry e = clientSet.get(session);
if(e == null) return null;
return restoreFromString(e.data);
} }
public Client getOrNewClient(UUID session) { public Client getOrNewClient(UUID session) {
return clientSet.computeIfAbsent(session, Client::new); Client client = getClient(session);
return client == null ? new Client(session) : client;
} }
public Client removeClient(UUID session) { public void removeClient(UUID session) {
return clientSet.remove(session); clientSet.remove(session);
} }
public void updateClient(UUID session) { public void updateClient(UUID session) {
Client c = clientSet.get(session); LogHelper.warning("Using deprecated method: sessionManager.updateClient");
if (c != null) {
c.up();
return;
}
Client newClient = new Client(session);
clientSet.put(session, newClient);
} }
public Set<Client> getSessions() { public Set<Client> getSessions() {
// TODO: removeme // TODO: removeme
return new HashSet<>(clientSet.values()); LogHelper.warning("Using deprecated method: sessionManager.getSession");
return new HashSet<>();
} }
public void loadSessions(Set<Client> set) { public void loadSessions(Set<Client> set) {
clientSet.putAll(set.stream().collect(Collectors.toMap(c -> c.session, Function.identity()))); LogHelper.warning("Using deprecated method: sessionManager.loadSessions");
//clientSet.putAll(set.stream().collect(Collectors.toMap(c -> c.session, Function.identity())));
}
private static class Entry {
public String data;
public long timestamp;
public Entry(String data) {
this.data = data;
this.timestamp = System.currentTimeMillis();
}
} }
} }

View file

@ -11,6 +11,7 @@
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.UUID; import java.util.UUID;
import java.util.concurrent.atomic.AtomicInteger;
public class Client { public class Client {
public UUID session; public UUID session;
@ -30,6 +31,8 @@ public class Client {
public transient Map<String, Object> properties; public transient Map<String, Object> properties;
public transient AtomicInteger refCount = new AtomicInteger(1);
public Client(UUID session) { public Client(UUID session) {
this.session = session; this.session = session;
timestamp = System.currentTimeMillis(); timestamp = System.currentTimeMillis();

View file

@ -37,7 +37,9 @@ public Client getClient() {
} }
public void setClient(Client client) { public void setClient(Client client) {
if(this.client != null) this.client.refCount.decrementAndGet();
this.client = client; this.client = client;
if(client != null) client.refCount.incrementAndGet();
} }
public final UUID getConnectUUID() { public final UUID getConnectUUID() {
@ -85,6 +87,7 @@ protected void channelRead0(ChannelHandlerContext ctx, WebSocketFrame frame) {
} else if ((frame instanceof PongWebSocketFrame)) { } else if ((frame instanceof PongWebSocketFrame)) {
LogHelper.dev("WebSocket Client received pong"); LogHelper.dev("WebSocket Client received pong");
} else if ((frame instanceof CloseWebSocketFrame)) { } else if ((frame instanceof CloseWebSocketFrame)) {
int statusCode = ((CloseWebSocketFrame) frame).statusCode();
ctx.channel().close(); ctx.channel().close();
} else { } else {
String message = "unsupported frame type: " + frame.getClass().getName(); String message = "unsupported frame type: " + frame.getClass().getName();
@ -95,6 +98,18 @@ protected void channelRead0(ChannelHandlerContext ctx, WebSocketFrame frame) {
@Override @Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception { public void channelInactive(ChannelHandlerContext ctx) throws Exception {
if (future != null) future.cancel(true); if (future != null) future.cancel(true);
if(LogHelper.isDevEnabled()) {
LogHelper.dev("Client %s disconnected", IOHelper.getIP(ctx.channel().remoteAddress()));
}
int refCount = client.refCount.decrementAndGet();
if(client.session != null) {
if(refCount == 0) {
srv.sessionManager.addClient(client);
}
else if(refCount < 0) {
LogHelper.warning("Client session %s reference counter invalid - %d", client.session, refCount);
}
}
super.channelInactive(ctx); super.channelInactive(ctx);
} }
} }

View file

@ -100,7 +100,7 @@ public void execute(ChannelHandlerContext ctx, Client clientData) throws Excepti
if (getSession) { if (getSession) {
if (clientData.session == null) { if (clientData.session == null) {
clientData.session = UUID.randomUUID(); clientData.session = UUID.randomUUID();
server.sessionManager.addClient(clientData); //server.sessionManager.addClient(clientData);
} }
result.session = clientData.session; result.session = clientData.session;
} }

View file

@ -21,15 +21,28 @@ public String getType() {
@Override @Override
public void execute(ChannelHandlerContext ctx, Client client) throws Exception { public void execute(ChannelHandlerContext ctx, Client client) throws Exception {
Client rClient = server.sessionManager.getClient(session); if(session == null) {
if (rClient == null) { sendError("Session invalid");
return;
}
final Client[] rClient = {null};
service.forEachActiveChannels((channel, handler) -> {
Client c = handler.getClient();
if(c != null && session.equals(c.session)) {
rClient[0] = c;
}
});
if(rClient[0] == null) {
rClient[0] = server.sessionManager.getClient(session);
}
if (rClient[0] == null) {
sendError("Session invalid"); sendError("Session invalid");
return; return;
} }
WebSocketFrameHandler frameHandler = ctx.pipeline().get(WebSocketFrameHandler.class); WebSocketFrameHandler frameHandler = ctx.pipeline().get(WebSocketFrameHandler.class);
frameHandler.setClient(rClient); frameHandler.setClient(rClient[0]);
if (needUserInfo) { if (needUserInfo) {
sendResult(new RestoreSessionRequestEvent(CurrentUserResponse.collectUserInfoFromClient(rClient))); sendResult(new RestoreSessionRequestEvent(CurrentUserResponse.collectUserInfoFromClient(rClient[0])));
} else { } else {
sendResult(new RestoreSessionRequestEvent()); sendResult(new RestoreSessionRequestEvent());
} }