This commit is contained in:
parent
5ca196e5d2
commit
6575af4665
@ -15,7 +15,8 @@
|
|||||||
#### Beta 1.0:
|
#### Beta 1.0:
|
||||||
- [ ] 基础世界树 API
|
- [ ] 基础世界树 API
|
||||||
+ [x] /authserver
|
+ [x] /authserver
|
||||||
+ [ ] /sessionserver
|
+ [x] /sessionserver
|
||||||
|
+ [ ] 测试
|
||||||
+ [ ] /api
|
+ [ ] /api
|
||||||
- [ ] 进阶 API
|
- [ ] 进阶 API
|
||||||
- [ ] 皮肤上传和安全检查
|
- [ ] 皮肤上传和安全检查
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
|
|
||||||
export async function headerValidation(req, rep) {
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,6 +3,7 @@ import { mongoose } from 'mongoose'
|
|||||||
import { registerModels } from './models/index.js';
|
import { registerModels } from './models/index.js';
|
||||||
import * as Hooks from './hooks.js'
|
import * as Hooks from './hooks.js'
|
||||||
import * as AuthenticateRoutings from './routes/authenticate.js'
|
import * as AuthenticateRoutings from './routes/authenticate.js'
|
||||||
|
import * as SessionServerRoutings from './routes/session.js'
|
||||||
import { config } from './config.js'
|
import { config } from './config.js'
|
||||||
|
|
||||||
export const server = fastify({
|
export const server = fastify({
|
||||||
@ -34,6 +35,9 @@ export const setup = async () => {
|
|||||||
server.route(AuthenticateRoutings.invalidate)
|
server.route(AuthenticateRoutings.invalidate)
|
||||||
server.route(AuthenticateRoutings.signout)
|
server.route(AuthenticateRoutings.signout)
|
||||||
|
|
||||||
|
server.route(SessionServerRoutings.join)
|
||||||
|
server.route(SessionServerRoutings.hasJoined)
|
||||||
|
|
||||||
config.custom.postRouting(server)
|
config.custom.postRouting(server)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import mongoose from 'mongoose'
|
import mongoose from 'mongoose'
|
||||||
const { Schema } = mongoose
|
const { Schema } = mongoose
|
||||||
|
import { uuidToNoSymboUUID } from '../generator.js'
|
||||||
|
|
||||||
export const PlayerSchema = new Schema({
|
export const PlayerSchema = new Schema({
|
||||||
username: String, // 有符号 UUID
|
username: String, // 有符号 UUID
|
||||||
@ -65,3 +66,37 @@ 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
|
let { username, password, clientToken, requestUser, agent } = req.body
|
||||||
|
|
||||||
if(!username || !password || !agent || agent.name.toLowerCase() !== 'minecraft') {
|
if(!username || !password || !agent || agent.name.toLowerCase() !== 'minecraft') {
|
||||||
rep.code(418).send({
|
return await rep.code(418).send({
|
||||||
error: "ForbiddenOperationException",
|
error: "ForbiddenOperationException",
|
||||||
errorMessage: "无效应用名,此服务端仅支援 Minecraft",
|
errorMessage: "无效应用名,此服务端仅支援 Minecraft",
|
||||||
cause: "此服务器只支持 agent.name: minecraft"
|
cause: "此服务器只支持 agent.name: minecraft"
|
||||||
@ -94,37 +94,7 @@ export const authenticate = {
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
const textures = {
|
const profile = PlayerModel.getPlayerSerialization(player)
|
||||||
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')
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
new this.models.Token({
|
new this.models.Token({
|
||||||
uuid: player.uuid,
|
uuid: player.uuid,
|
||||||
@ -257,37 +227,7 @@ export const refresh = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if(selectedProfile) {
|
if(selectedProfile) {
|
||||||
const textures = {
|
response.selectedProfile = PlayerModel.getPlayerSerialization(player)
|
||||||
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')
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
162
src/routes/session.js
Normal file
162
src/routes/session.js
Normal 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))
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user