I'm developing desktop software with JavaFX and Java Spark which is basically a barebones framework for developing web apps, but I'm trying to use it strictly to put/get sensitive methods and variables on a server so that users can't access them. REST seems to be the correct approach but I'm struggling to understand 2 interrelated REST concepts. The Spark docs are light but I've integrated the few good tutorials into a working demo below but I've hit a wall. I'll briefly explain:
With the help of Postman I've been able to put
a few records onto the server by using a path of http://localhost:4567/secrets
and Body of:
{
"title" : "demofield1",
"content" : "12345"
}
Each record contains a title as an identifier (demofield1) and content as the sensitive data that should remain hidden from users at all times (12345). It's pretty trivial to put
these strings onto a server and then get
them by using title as a parameter, shown below. The demo code simply has a Model class for creating and returning a record (secret), a JSON conversion method, and a get
and put
Spark method. Secrets are stored locally in a HashMap for now, I'm assuming a real app would simply swap in a server DB.
The get
method works as expected, returning the correct JSON record and storing the content as a String with this line: String secretString = model.getCertainSecret(title).getContent();
With that said...
Questions (partial answers fully appreciated too):
secretString
above now holds a confidential value (12345) which is obtained using a supposedly secure REST method. But couldn't a user simply reverse-engineer my source code and writeSystem.out.println(secretString)
and have that 12345 revealed? I don't understand how a simple string is protected after retrieving it from the server, despite not being explicitly shown. The code seems correct yet the value is easily obtainable. What am I missing?How do you
put
entire java methods on a server? A lot of code I need to protect isn't just strings but methods containingTasks
,Platform.runLater()->
, and needs to interact with other desktop software. For example, one of my methods uses JACOB to identify when a certain area of a third-party software is clicked. I can't even fathom what a get/put would look like in that context.
My assumption was that a server-side DB would store all content from my put
requests, but I don't understand how it stores and returns a method? Should I be reading about servlets or SaaS or something? I'm focused on desktop users.
Code:
import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import lombok.Data;
import org.apache.log4j.BasicConfigurator;
import java.io.IOException;
import java.io.StringWriter;
import java.util.HashMap;
import java.util.Map;
import static spark.Spark.get;
import static spark.Spark.put;
public class ServerDemo
{
private static final int HTTP_BAD_REQUEST = 400;
@Data
static class NewSecretPayload {
private String title;
private String content;
public boolean isValid() {
return title != null && !title.isEmpty();
}
}
public static class Model {
private int nextId = 1;
private Map<String, Secret> secrets = new HashMap<>();
@Data
class Secret {
private int id;
private String title;
private String content;
}
public int createSecret(String title, String content){
int id = nextId++;
Secret secret = new Secret();
secret.setId(id);
secret.setTitle(title);
secret.setContent(content);
secrets.put(title, secret);
return id;
}
public Secret getCertainSecret(String titleToUse){
if(null != secrets.get(titleToUse)){
return secrets.get(titleToUse);
}else{
return null;
}
}
}
public static String dataToJson(Object data) {
try {
ObjectMapper mapper = new ObjectMapper();
mapper.enable(SerializationFeature.INDENT_OUTPUT);
StringWriter sw = new StringWriter();
mapper.writeValue(sw, data);
return sw.toString();
} catch (IOException e) {
throw new RuntimeException("IOException from a StringWriter?");
}
}
public static void main(String[] args) {
Model model = new Model();
BasicConfigurator.configure();
put("/secrets", (request, response) -> {
try {
ObjectMapper mapper = new ObjectMapper();
NewSecretPayload creation = mapper.readValue(request.body(), NewSecretPayload.class);
if (!creation.isValid()) {
response.status(HTTP_BAD_REQUEST);
return "";
}
int id = model.createSecret(creation.getTitle(), creation.getContent());
response.status(200);
response.type("application/json");
return id;
} catch (JsonParseException jpe) {
response.status(HTTP_BAD_REQUEST);
return "";
}
});
get("/secrets/:title", (req, res) -> {
String title = req.params(":title");
if (model.getCertainSecret(title) != null) {
res.status(200);
res.type("application/json");
String secretString = model.getCertainSecret(title).getContent();
return dataToJson(model.getCertainSecret(title));
}
res.status(400);
return new ResponseError("No user with title "+title+" was found", title);
});
}
}