实现 sessionserver
continuous-integration/drone/push Build is failing
Details
continuous-integration/drone/push Build is failing
Details
This commit is contained in:
parent
5ca196e5d2
commit
6575af4665
|
@ -15,7 +15,8 @@
|
|||
#### Beta 1.0:
|
||||
- [ ] 基础世界树 API
|
||||
+ [x] /authserver
|
||||
+ [ ] /sessionserver
|
||||
+ [x] /sessionserver
|
||||
+ [ ] 测试
|
||||
+ [ ] /api
|
||||
- [ ] 进阶 API
|
||||
- [ ] 皮肤上传和安全检查
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
||||
/*
|
||||
|
|
|
@ -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')
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue