getstream-io中通过Channel ID加入频道的权限问题
我正在开发一款基于Stream API(getstream-io)的双人游戏应用,首次使用该API。应用支持旁观者创建游戏,用频道承载游戏,选用了messaging类型,生成的6位GameID作为频道ID,希望玩家仅通过GameID就能加入游戏。
当前代码
CreateGame.jsx
import React, { useState } from "react"; import { useChatContext, Channel } from "stream-chat-react"; import Split_or_Steal from "./Split_or_steal"; import Cookies from "universal-cookie"; function getRandomInt(max) { return Math.floor(Math.random() * max); } function CreateGame() { const cookies = new Cookies(); const [gameID, setGameID] = useState(""); const { client } = useChatContext(); const [channel, setChannel] = useState(null); const createChannel = async () => { const gameID=getRandomInt(1000000) const newChannel = client.channel('messaging', gameID , { members: [cookies.get("userId")], session: 3, show: false, player1_input: null, player2_input: null }); await newChannel.watch(); setChannel(newChannel); }; return ( <> {channel ? ( <Channel channel={channel}> <Split_or_Steal channel={channel} setChannel={setChannel}/> </Channel> ) : ( <div className=""> <button className=" w-52 h-24 border rounded-md bg-white text-black text-bold text-4xl hover:bg-blue-600 hover:text-white" onClick={createChannel}> CREATE </button> </div> )} </> ); } export default CreateGame;
JoinGame.jsx
import React, { useState } from "react"; import { useChatContext, Channel } from "stream-chat-react"; import Split_or_Steal from "./Split_or_steal"; import Cookies from "universal-cookie"; function JoinGame() { const cookies = new Cookies(); const [gameID, setGameID] = useState(""); const { client } = useChatContext(); const [channel, setChannel] = useState(null); const joinChannel = async () => { const filter = { type: 'messaging', id: gameID }; const sort = [{ last_message_at: -1 }]; const channels = await client.queryChannels(filter, sort, { watch: true, // this is the default state: true, }); if (channels.length ===0 ){ alert("channel not found"); return; } const joinedChannel=channels[0]; await joinedChannel.watch(); await joinedChannel.addMembers([cookies.get("userId")]); setChannel(joinedChannel); console.log("joined channel") }; return ( <> {channel ? ( <Channel channel={channel}> <Split_or_Steal channel={channel} setChannel={setChannel}/> </Channel> ) : ( <div className=""> <h4>Join Game</h4> <input placeholder="Game ID" onChange={(event) => { setGameID(event.target.value); }} /> <button onClick={joinChannel}> Join </button> </div> )} </> ); } export default JoinGame;
遇到的错误
只有创建频道的账号能通过GameID找到并加入频道,其他账号加入时触发错误:
Uncaught (in promise) Error: StreamChat error code 70: QueryChannels failed with error: "1 channels match your query but cannot be returned because you don't have access to them. Did you forget to include {members: $in: ["fec7bc90-260e-41c4-bef1-1dbe695e082b"]}?"
本质是非创建用户无权限自行加入频道,需要解决办法。
解决方案
核心原因
Stream的messaging类型频道默认仅允许成员查询和访问,非成员无法通过queryChannels接口获取频道信息,因此直接查询会触发权限错误。
具体解决步骤
1. 改用直接初始化频道对象,绕过查询接口
不需要通过queryChannels查询频道,直接用GameID(频道ID)初始化频道对象,再尝试加入,这样可以跳过查询权限限制:
修改后的JoinGame.jsx:
import React, { useState } from "react"; import { useChatContext, Channel } from "stream-chat-react"; import Split_or_Steal from "./Split_or_steal"; import Cookies from "universal-cookie"; function JoinGame() { const cookies = new Cookies(); const [gameID, setGameID] = useState(""); const { client } = useChatContext(); const [channel, setChannel] = useState(null); const joinChannel = async () => { try { // 直接用GameID初始化频道对象 const targetChannel = client.channel('messaging', gameID); // 尝试监听频道并添加自己为成员 await targetChannel.watch(); await targetChannel.addMembers([cookies.get("userId")]); setChannel(targetChannel); console.log("joined channel"); } catch (error) { // 针对性处理错误 if (error.code === 70 || error.message.includes("access")) { alert("无法加入该游戏,请检查GameID或确认游戏状态"); } else if (error.code === 5) { alert("游戏不存在"); } else { alert("加入失败:" + error.message); } } }; return ( <> {channel ? ( <Channel channel={channel}> <Split_or_Steal channel={channel} setChannel={setChannel}/> </Channel> ) : ( <div className=""> <h4>Join Game</h4> <input placeholder="Game ID" onChange={(event) => { setGameID(event.target.value); }} /> <button onClick={joinChannel}> Join </button> </div> )} </> ); } export default JoinGame;
2. 优化频道创建的基础设置
创建频道时,确保GameID是字符串类型(Stream频道ID推荐用字符串),并添加created_by字段,方便后续权限控制:
修改CreateGame.jsx中的createChannel函数:
const createChannel = async () => { // 生成6位字符串格式的GameID,补零确保位数一致 const gameID = getRandomInt(1000000).toString().padStart(6, '0'); const newChannel = client.channel('messaging', gameID, { members: [cookies.get("userId")], created_by: cookies.get("userId"), session: 3, show: false, player1_input: null, player2_input: null }); await newChannel.watch(); setChannel(newChannel); };
3. 调整Stream后台权限(可选)
如果需要更开放的权限策略,可以在Stream Dashboard的Chat -> Permissions中,修改messaging类型频道的权限规则:
- 允许非成员查询频道
- 允许用户添加自己为频道成员(需结合业务场景评估安全性)
额外建议
- 安全校验:如果需要防止恶意加入,可以在后端添加GameID验证逻辑,前端通过后端接口完成加入操作,避免直接在前端处理权限
- 频道状态管理:可以在频道自定义字段中添加
max_players、game_status等信息,控制加入逻辑(比如游戏满员时拒绝加入)
内容的提问来源于stack exchange,提问作者Louis VERAN

