浅谈 RedeSky 服务器验证机制

本文最后更新于:2021年10月27日 晚上

前几天在玩 RedeSky 服务器的时候,发现这个服务器虽然是离线验证,但需要去官网注册一个论坛账号才能玩;而这个账号的密码对应的就是进服务器 /login 的密码。

于是我对这个验证机制产生了兴趣,因为在此之前我没见过类似这种验证方式的服务器。

我想这种服务器的安全性一定很好,不仅因为注册论坛账号需要邮箱,并且一个邮箱只能注册一个账号;而且因为这个服务器的反作弊很好,封禁直接连带 IP、论坛账号一同封禁。

所以我突发奇想,想做一个类似这种验证方式的服务器插件。顺便测试一下我的 Java 水平怎么样。

思路和简单实现

既然是网页注册,那么我们的插件就一定具备简易的 Web 服务器 的功能。于是我翻阅资料,知道可以使用 java.net.ServerSocketjava.net.Socket 来实现这个功能。

1
2
3
4
5
6
7
8
9
10
11
public WebServer(int port) {
this.port = port;
}

public void startServer() {
try {
this.serverSocket = new ServerSocket(this.port);
} catch (IOException e) { // 可能会抛出 IOException
e.printStackTrace(); // 端口被占用就会启动失败
}
}

Socket 服务器 建立完毕了,接下来是?当然是接受请求啦!写个 while 循环来接受请求:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public void await() throws IOException {
// 循环等待 request 请求
while (true) {
Socket socket;
InputStream inputStream;
OutputStream outputStream;
try {
socket = this.serverSocket.accept(); // Socket 对象
inputStream = socket.getInputStream(); // 接收到的请求的内容
outputStream = socket.getOutputStream(); // 返回的内容

// 解析 inputStream 中的内容
// ···

// 解析完关闭 Socket 对象
socket.shutdownOutput();
socket.close();
} catch (StopServer e) { // 循环结束的标志是解析过程中抛出 StopServer
break;
} catch (IOException e) {
e.printStackTrace();
}
}
this.serverSocket.close(); // 关闭 Socket 服务器
}

网页注册等请避免使用 GET 请求,请使用 POST

outputStream 的内容应该是这样:

1
2
3
4
HTTP/1.1 200
Content-type:text/json;charset=UTF-8

{"code":200,"success":true}

返回一个 Json 字符串,告诉是否注册成功,方便网站和登录验证。

紧接着,将这个注册的数据保存在数据库中,并放到内存里等待玩家登录的时候使用。

接下来就是等玩家登录了,使用 org.bukkit.event.player.AsyncPlayerPreLoginEvent ,然后就和普通的登录插件差不多。将玩家名和密码与数据库中的论坛名和密码对比,如果相同则通过验证。

后话

这么说,感觉实现这个插件并不是很难;但是,我为了省事就直接用明文储存密码了。

千万不要偷懒用明文储存密码,因为这是非常危险的![1]

这个插件这样子就算完工了,不过还是有一些 Bug,可能时不时来一个 NullPointerException(笑),所以我就不放 Github 了…

不管了,代码和人有一个能跑就行 (ノ ̄▽ ̄)~

参考