实现 sessionserver
continuous-integration/drone/push Build is failing Details

This commit is contained in:
Qumolama.d 2022-05-09 22:09:08 +08:00
parent 5ca196e5d2
commit 6575af4665
Signed by: Lama3L9R
GPG Key ID: 1762AFC05157CE18
6 changed files with 207 additions and 65 deletions

View File

@ -15,7 +15,8 @@
#### Beta 1.0:
- [ ] 基础世界树 API
+ [x] /authserver
+ [ ] /sessionserver
+ [x] /sessionserver
+ [ ] 测试
+ [ ] /api
- [ ] 进阶 API
- [ ] 皮肤上传和安全检查

View File

@ -1,6 +1,6 @@
export async function headerValidation(req, rep) {
if(!(/(authserver)|(sessionserver)|(api)/g).test(req.url)) {
if(!(/(authserver)|(sessionserver)|(api)/g).test(req.url) || req.method !== 'POST') {
return
}

View File

@ -3,6 +3,7 @@ import { mongoose } from 'mongoose'
import { registerModels } from './models/index.js';
import * as Hooks from './hooks.js'
import * as AuthenticateRoutings from './routes/authenticate.js'
import * as SessionServerRoutings from './routes/session.js'
import { config } from './config.js'
export const server = fastify({
@ -34,6 +35,9 @@ export const setup = async () => {
server.route(AuthenticateRoutings.invalidate)
server.route(AuthenticateRoutings.signout)
server.route(SessionServerRoutings.join)
server.route(SessionServerRoutings.hasJoined)
config.custom.postRouting(server)
/*

View File

@ -1,5 +1,6 @@
import mongoose from 'mongoose'
const { Schema } = mongoose
import { uuidToNoSymboUUID } from '../generator.js'
export const PlayerSchema = new Schema({
username: String, // 有符号 UUID
@ -64,4 +65,38 @@ export const PlayerAccountSerializationSchema = {
}
}
}
}
export function getPlayerSerialization(player) {
const textures = {
timestamp: 0,
profileId: uuidToNoSymboUUID(player.uuid),
profileName: player.username,
textures: { }
}
if(player.textures.skin && player.textures.skin != 0) { // Must be '!=' if this change to '!==' will never works
textures.textures.SKIN = {
url: player.textures.skin,
metadata
}
}
if(player.textures.skin && player.textures.skin != 0) { // Must be '!=' if this change to '!==' will never works
textures.textures.CAPE = {
url: player.textures.cape,
metadata
}
}
return {
uuid: uuidToNoSymboUUID(player.uuid),
name: player.username,
properties: [
{
name: "texturs",
value: Buffer.from(JSON.stringify(textures)).toString('base64')
}
]
}
}

View File

@ -60,7 +60,7 @@ export const authenticate = {
let { username, password, clientToken, requestUser, agent } = req.body
if(!username || !password || !agent || agent.name.toLowerCase() !== 'minecraft') {
rep.code(418).send({
return await rep.code(418).send({
error: "ForbiddenOperationException",
errorMessage: "无效应用名,此服务端仅支援 Minecraft",
cause: "此服务器只支持 agent.name: minecraft"
@ -94,37 +94,7 @@ export const authenticate = {
]
}
const textures = {
timestamp: 0,
profileId: uuidToNoSymboUUID(player.uuid),
profileName: player.username,
textures: { }
}
if(player.textures.skin && player.textures.skin != 0) { // Must be '!=' if this change to '!==' will never works
textures.textures.SKIN = {
url: player.textures.skin,
metadata
}
}
if(player.textures.skin && player.textures.skin != 0) { // Must be '!=' if this change to '!==' will never works
textures.textures.CAPE = {
url: player.textures.cape,
metadata
}
}
const profile = {
uuid: uuidToNoSymboUUID(player.uuid),
name: player.username,
properties: [
{
name: "texturs",
value: Buffer.from(JSON.stringify(textures)).toString('base64')
}
]
}
const profile = PlayerModel.getPlayerSerialization(player)
new this.models.Token({
uuid: player.uuid,
@ -257,37 +227,7 @@ export const refresh = {
}
if(selectedProfile) {
const textures = {
timestamp: 0,
profileId: uuidToNoSymboUUID(player.uuid),
profileName: player.username,
textures: { }
}
if(player.textures.skin && player.textures.skin != 0) { // Must be '!=' if this change to '!==' will never works
textures.textures.SKIN = {
url: player.textures.skin,
metadata
}
}
if(player.textures.skin && player.textures.skin != 0) { // Must be '!=' if this change to '!==' will never works
textures.textures.CAPE = {
url: player.textures.cape,
metadata
}
}
response.selectedProfile = {
uuid: uuidToNoSymboUUID(uuid),
name: player.username,
properties: [
{
name: "texturs",
value: Buffer.from(JSON.stringify(textures)).toString('base64')
}
]
}
response.selectedProfile = PlayerModel.getPlayerSerialization(player)
}
}

162
src/routes/session.js Normal file
View File

@ -0,0 +1,162 @@
import { getOverrideHandler, getOverridePreHandler } from '../config.js'
import { toSymboUUID } from '../generator'
import { getPlayerSerialization, PlayerSeriliazationSchema } from '../models/player'
/*
Key: string Username
Value: {
accessToken: string,
serverId: string,
ip: string
}
*/
const joinServerRequest = new Map()
export const join = {
method: 'POST',
url: '/sessionserver/session/minecraft/join',
schema: {
body: {
"accessToken": {
"type": "string"
},
"selectedProfile": {
"type": "string"
},
"serverId": {
"type": "string"
},
},
response: {
204: {
type: "null"
}
}
},
preHandler: getOverridePreHandler('/sessionserver/session/minecraft/join'),
handler: getOverrideHandler('/sessionserver/session/minecraft/join') ?? async function (req, rep) {
const { accessToken, selectedProfile, serverId } = req.body
const user = await await this.models.Player.findOne({ uuid: toSymboUUID(selectedProfile) })
if (!user) {
return await rep.code(400).send({
error: "IllegalArgumentException",
errorMessage: "请求内容不正确",
cause: "用户不存在"
})
}
const session = await this.models.Session.findOne({ token: accessToken })
if (!session) {
return await rep.code(401).send({
error: "IllegalArgumentException",
errorMessage: "无效会话",
cause: "无效会话"
})
}
if (Date.now() > session.expireDate) {
return await rep.code(401).send({
error: "IllegalArgumentException",
errorMessage: "无效会话",
cause: "会话已过期"
})
}
joinServerRequest.set(session.uuid, {
accessToken,
serverId,
ip: req.headers['x-forwarded-for'] || req.info.remoteAddress
})
await rep.code(204).send()
}
}
export const hasJoined = {
method: 'GET',
url: '/sessionserver/session/minecraft/hasJoined',
schema: {
query: {
"username": {
"type": "string"
},
"serverId": {
"type": "string"
},
"ip": {
"type": "string"
}
},
response: {
200: PlayerSeriliazationSchema,
204: {
type: "null"
}
}
},
preHandler: getOverridePreHandler('/sessionserver/session/minecraft/hasJoined'),
handler: getOverrideHandler('/sessionserver/session/minecraft/hasJoined') ?? async function (req, rep) {
const { username, serverId, ip } = req.query
const { ssID, sIP } = joinServerRequest.get(username)
if(ip) {
if(ip !== sIP) {
return await rep.code(401).send({
error: "IllegalArgumentException",
errorMessage: "无效会话",
cause: "IP 不匹配"
})
}
}
if(serverId !== ssID) {
return await rep.code(401).send({
error: "IllegalArgumentException",
errorMessage: "无效会话",
cause: "服务器 ID 不匹配"
})
}
const player = await this.models.Player.findOne({ uuid: toSymboUUID(username) })
if (!player) {
return await rep.code(400).send({
error: "IllegalArgumentException",
errorMessage: "请求内容不正确",
cause: "用户不存在"
})
}
await rep.code(200).send(getPlayerSerialization(player))
}
}
export const profile = {
method: 'GET',
url: '/sessionserver/session/minecraft/profile/:uuid',
schema: {
params: {
"uuid": {
"type": "string"
},
unsigned: {
"type": ["boolean", "null"]
},
},
response: {
200: PlayerSeriliazationSchema,
204: {
type: "null"
}
}
},
preHandler: getOverridePreHandler('/sessionserver/session/minecraft/profile/:uuid'),
handler: getOverrideHandler('/sessionserver/session/minecraft/profile/:uuid') ?? async function (req, rep) {
const { uuid } = req.params
const player = await this.models.Player.findOne({ uuid: toSymboUUID(uuid) })
if (!player) {
return await rep.code(204).send()
}
await rep.code(200).send(getPlayerSerialization(player))
}
}