[Feature]: Login Endpunkt und Session Store #4
3 changed files with 144 additions and 0 deletions
52
api/api.yml
52
api/api.yml
|
@ -39,6 +39,34 @@ components:
|
|||
- username
|
||||
- password
|
||||
#############################################
|
||||
# PlayerLoginData #
|
||||
#############################################
|
||||
PlayerLoginData:
|
||||
description: Data needed to log a Player in
|
||||
type: object
|
||||
properties:
|
||||
username:
|
||||
type: string
|
||||
password:
|
||||
type: string
|
||||
required:
|
||||
- username
|
||||
- password
|
||||
#############################################
|
||||
# PlayerLoginSession #
|
||||
#############################################
|
||||
PlayerLoginSession:
|
||||
description: Data needed to log a Player in
|
||||
type: object
|
||||
properties:
|
||||
username:
|
||||
type: string
|
||||
token:
|
||||
type: string
|
||||
required:
|
||||
- username
|
||||
- token
|
||||
#############################################
|
||||
# AdminAuthInfo #
|
||||
#############################################
|
||||
ServerHealth:
|
||||
|
@ -61,6 +89,8 @@ components:
|
|||
responses:
|
||||
201PlayerCreated:
|
||||
description: "201 - Player Created"
|
||||
401PlayerNameOrPasswordWrong:
|
||||
description: "401 - Player Name or Password is Wrong"
|
||||
401Unauthorized:
|
||||
description: "401 - Unauthorized"
|
||||
404NotFound:
|
||||
|
@ -112,6 +142,28 @@ paths:
|
|||
$ref: "#/components/responses/409UsernameTaken"
|
||||
500:
|
||||
$ref: "#/components/responses/500InternalError"
|
||||
/player/login:
|
||||
post:
|
||||
operationId: "PlayerLogin"
|
||||
tags:
|
||||
- server
|
||||
description: "Endpoint for logging a Player in"
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/PlayerLoginData"
|
||||
responses:
|
||||
200:
|
||||
description: "A Login Session, which can be used in the Webhook"
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/PlayerLoginSession"
|
||||
401:
|
||||
$ref: "#/components/responses/401PlayerNameOrPasswordWrong"
|
||||
500:
|
||||
$ref: "#/components/responses/500InternalError"
|
||||
/server/health:
|
||||
get:
|
||||
operationId: "ServerGetHealthcheck"
|
||||
|
|
|
@ -0,0 +1,67 @@
|
|||
package de.towerdefence.server.player.session;
|
||||
|
||||
import de.towerdefence.server.player.Player;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.SecureRandom;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@Component
|
||||
public class PlayerLoginSessions {
|
||||
public static final int PLAYER_LOGIN_SESSION_TOKEN_BYTE_LENGTH = 64;
|
||||
private final SecureRandom random;
|
||||
private final Map<String, String> playerLoginSessionTokens = new HashMap<>();
|
||||
private final Map<String, Player> playerLoginSessionPlayers = new HashMap<>();
|
||||
private final Map<String, ScheduledFuture<?>> playerLoginSessionSchedule = new HashMap<>();
|
||||
private static final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
|
||||
|
||||
public PlayerLoginSessions() {
|
||||
random = new SecureRandom();
|
||||
}
|
||||
|
||||
public String createPlayerLoginSession(Player player) {
|
||||
byte[] token_data = new byte[PLAYER_LOGIN_SESSION_TOKEN_BYTE_LENGTH];
|
||||
this.random.nextBytes(token_data);
|
||||
String token = new String(token_data, StandardCharsets.UTF_8);
|
||||
String playerName = player.getUsername();
|
||||
this.playerLoginSessionTokens.put(playerName, token);
|
||||
this.playerLoginSessionPlayers.put(playerName, player);
|
||||
this.playerLoginSessionSchedule.put(playerName, scheduler.schedule(
|
||||
() -> {
|
||||
this.playerLoginSessionTokens.remove(playerName);
|
||||
this.playerLoginSessionPlayers.remove(playerName);
|
||||
this.playerLoginSessionSchedule.remove(playerName);
|
||||
},
|
||||
30,
|
||||
TimeUnit.SECONDS
|
||||
));
|
||||
return token;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return an Optional Player. If it is empty, that Player has no valid Login Session
|
||||
*/
|
||||
public Optional<Player> getPlayerFromLoginSession(String username, String token) {
|
||||
if (!this.playerLoginSessionTokens.containsKey(username)) {
|
||||
return Optional.empty();
|
||||
}
|
||||
if (!this.playerLoginSessionTokens.get(username).equals(token)) {
|
||||
return Optional.empty();
|
||||
}
|
||||
this.playerLoginSessionTokens.remove(username);
|
||||
Player player = this.playerLoginSessionPlayers.get(username);
|
||||
ScheduledFuture<?> task = this.playerLoginSessionSchedule.get(username);
|
||||
if (task != null) {
|
||||
task.cancel(true);
|
||||
}
|
||||
this.playerLoginSessionSchedule.remove(username);
|
||||
return Optional.of(player);
|
||||
}
|
||||
}
|
|
@ -2,11 +2,14 @@ package de.towerdefence.server.server;
|
|||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import de.towerdefence.server.oas.ServerApi;
|
||||
import de.towerdefence.server.oas.models.PlayerLoginData;
|
||||
import de.towerdefence.server.oas.models.PlayerLoginSession;
|
||||
import de.towerdefence.server.oas.models.PlayerRegistrationData;
|
||||
import de.towerdefence.server.oas.models.ServerHealth;
|
||||
import de.towerdefence.server.player.Player;
|
||||
import de.towerdefence.server.player.PlayerRepository;
|
||||
import de.towerdefence.server.player.PlayerService;
|
||||
import de.towerdefence.server.player.session.PlayerLoginSessions;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.HttpStatus;
|
||||
|
@ -25,6 +28,8 @@ public class ServerApiController implements ServerApi {
|
|||
private PlayerRepository playerRepository;
|
||||
@Autowired
|
||||
private PlayerService playerService;
|
||||
@Autowired
|
||||
private PlayerLoginSessions playerLoginSessions;
|
||||
|
||||
@Override
|
||||
public Optional<ObjectMapper> getObjectMapper() {
|
||||
|
@ -52,6 +57,26 @@ public class ServerApiController implements ServerApi {
|
|||
return new ResponseEntity<>(HttpStatus.CREATED);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResponseEntity<PlayerLoginSession> playerLogin(PlayerLoginData body) {
|
||||
Player player = playerRepository.findByUsername(body.getUsername());
|
||||
if(player == null){
|
||||
return new ResponseEntity<>(HttpStatus.UNAUTHORIZED);
|
||||
}
|
||||
try {
|
||||
if(!playerService.checkPassword(player, body.getPassword())) {
|
||||
return new ResponseEntity<>(HttpStatus.UNAUTHORIZED);
|
||||
}
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
String token = playerLoginSessions.createPlayerLoginSession(player);
|
||||
PlayerLoginSession session = new PlayerLoginSession();
|
||||
session.setUsername(player.getUsername());
|
||||
session.setToken(token);
|
||||
return new ResponseEntity<>(session, HttpStatus.OK);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResponseEntity<ServerHealth> serverGetHealthcheck() {
|
||||
ServerHealth health = new ServerHealth();
|
||||
|
|
Loading…
Add table
Reference in a new issue