danmaku: WS join 强制 live-token 校验并锁定昵称(服务端校验 user,忽略客户端声明)
This commit is contained in:
@@ -0,0 +1 @@
|
|||||||
|
{"verify_url": "https://api.rrrosmontis.icu/v1/live/api/auth/verify"}
|
||||||
@@ -17,6 +17,34 @@ logging.basicConfig(
|
|||||||
]
|
]
|
||||||
)
|
)
|
||||||
logger = logging.getLogger("danmaku")
|
logger = logging.getLogger("danmaku")
|
||||||
|
MSG_NO_LOGIN = "未登录,无法加入弹幕"
|
||||||
|
MSG_BAD_TOKEN = "登录已失效,请刷新页面重试"
|
||||||
|
NICK_GUEST = "观众"
|
||||||
|
|
||||||
|
# ==================== 弹幕登录校验(live token -> live-api) ====================
|
||||||
|
import os as _os
|
||||||
|
VERIFY_URL = 'https://api.rrrosmontis.icu/v1/live/api/auth/verify'
|
||||||
|
try:
|
||||||
|
_cfg = json.load(open(_os.path.join(_os.path.dirname(_os.path.abspath(__file__)), 'danmaku_auth.json'), encoding='utf-8'))
|
||||||
|
VERIFY_URL = _cfg.get('verify_url', VERIFY_URL)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def _verify_token_sync(token):
|
||||||
|
try:
|
||||||
|
import urllib.request as _ur
|
||||||
|
req = _ur.Request(VERIFY_URL, data=json.dumps({'token': token}).encode('utf-8'), headers={'Content-Type': 'application/json'})
|
||||||
|
with _ur.urlopen(req, timeout=6) as resp:
|
||||||
|
d = json.loads(resp.read().decode('utf-8'))
|
||||||
|
return d.get('user') if d.get('ok') else None
|
||||||
|
except Exception:
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
async def _verify_token(token):
|
||||||
|
return await asyncio.to_thread(_verify_token_sync, token)
|
||||||
|
|
||||||
|
|
||||||
# ==================== 直播间类 ====================
|
# ==================== 直播间类 ====================
|
||||||
class LiveRoom:
|
class LiveRoom:
|
||||||
@@ -56,6 +84,7 @@ app = FastAPI()
|
|||||||
async def websocket_endpoint(websocket: WebSocket):
|
async def websocket_endpoint(websocket: WebSocket):
|
||||||
await websocket.accept()
|
await websocket.accept()
|
||||||
client_host = websocket.client.host if websocket.client else "unknown"
|
client_host = websocket.client.host if websocket.client else "unknown"
|
||||||
|
token = (websocket.query_params.get('token') or '').strip()
|
||||||
logger.info(f"新WebSocket连接: {client_host}")
|
logger.info(f"新WebSocket连接: {client_host}")
|
||||||
|
|
||||||
current_room: LiveRoom = None
|
current_room: LiveRoom = None
|
||||||
@@ -82,8 +111,19 @@ async def websocket_endpoint(websocket: WebSocket):
|
|||||||
current_room.connections.discard(websocket)
|
current_room.connections.discard(websocket)
|
||||||
|
|
||||||
# 加入新房间
|
# 加入新房间
|
||||||
|
# 登录校验: 弹幕连接必须携带有效 live token (?token=)
|
||||||
|
if not token:
|
||||||
|
await websocket.send_json({"type": "error", "message": MSG_NO_LOGIN})
|
||||||
|
await websocket.close(code=4401)
|
||||||
|
return
|
||||||
|
user_info = await _verify_token(token)
|
||||||
|
if not user_info:
|
||||||
|
await websocket.send_json({"type": "error", "message": MSG_BAD_TOKEN})
|
||||||
|
await websocket.close(code=4401)
|
||||||
|
return
|
||||||
|
|
||||||
current_room = await get_or_create_room(room_id)
|
current_room = await get_or_create_room(room_id)
|
||||||
current_user = data.get("user", "匿名")
|
current_user = user_info.get("nickname") or NICK_GUEST
|
||||||
async with current_room.connections_lock:
|
async with current_room.connections_lock:
|
||||||
current_room.connections.add(websocket)
|
current_room.connections.add(websocket)
|
||||||
|
|
||||||
@@ -106,7 +146,7 @@ async def websocket_endpoint(websocket: WebSocket):
|
|||||||
continue
|
continue
|
||||||
|
|
||||||
text = data.get("text", "")
|
text = data.get("text", "")
|
||||||
user = data.get("user", current_user or "匿名")
|
user = current_user or NICK_GUEST
|
||||||
danmaku = {
|
danmaku = {
|
||||||
"text": text,
|
"text": text,
|
||||||
"user": user,
|
"user": user,
|
||||||
|
|||||||
Reference in New Issue
Block a user