You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Vue项目中使用@azure/web-pubsub无法生成WebPubSubClient的clientAccessUri

Vue.js集成Azure Web PubSub时的JWT KeyObject实例判断错误

我在Vue.js应用中尝试用Azure Web PubSub实现动态群组的发布订阅功能,先解决了构建阶段的crypto/stream/Buffer/child_process未定义问题——通过安装crypto-browserify、stream-browserify、buffer包,并在webpack.config.js中添加fallback配置:

fallback: { // not present by default
    "crypto": require.resolve("crypto-browserify"),
    "stream": require.resolve("stream-browserify"),
    "buffer": require.resolve("buffer"),
    //"util": false,
    "child_process": false,
}

项目构建成功后,执行以下两类操作时均报错:"Right-hand side of 'instanceof' is not an object",错误来自jsonwebtoken/sign.js第108行:

if (secretOrPrivateKey != null && !(secretOrPrivateKey instanceof KeyObject)) {
    try {
      secretOrPrivateKey = createPrivateKey(secretOrPrivateKey)
    } catch (_) {
      try {
        secretOrPrivateKey = createSecretKey(typeof secretOrPrivateKey === 'string' ? Buffer.from(secretOrPrivateKey) : secretOrPrivateKey)
      } catch (_) {
        return failure(new Error('secretOrPrivateKey is not valid key material'));
      }
    }
}

注:未单独安装jsonwebtoken,它是@azure/identity的依赖。

错误场景1:获取客户端访问URL创建WebPubSubClient

const key = new AzureKeyCredential(WPS_APIKEY);
const serviceClient = new WebPubSubServiceClient(WPS_ENDPOINT, key, hubName);
const fetchClientAccessUrl = async (_: GetClientAccessUrlOptions) => {
    return (
        await serviceClient.getClientAccessToken({
            roles: [`webpubsub.joinLeaveGroup.${groupName}`, `webpubsub.sendToGroup.${groupName}`],
        })
    ).url;
};
let client = new WebPubSubClient({
    getClientAccessUrl: fetchClientAccessUrl,
} as WebPubSubClientCredential);

client.on("connected", (e) => {
    console.log(`Connection ${e.connectionId} is connected.`);
});

client.on("disconnected", (e) => {
    console.log(`Connection disconnected: ${e.message}`);
});
await client.start();
await client.joinGroup(groupName);
await client.sendToGroup(groupName, {
    conversationId: this.$data.conversation.conversationId,
    conversationContext: this.$data.conversation.conversationContext,
    message: {
        messageId: 100,
        message: `Hello, You are welcome to the group: ${groupName}`,
        timestamp: "08-06 06:20 PM",
        sender: this.user.userName
    }
}, "json");

错误场景2:动态创建群组并添加用户

const key = new AzureKeyCredential(WPS_APIKEY);
const chatHub = new WebPubSubServiceClient(WPS_ENDPOINT, key, hubName);
let groupExists = await checkGroupExists(groupName);
if (!groupExists) {
    let group = await chatHub.group(groupName);
    users.forEach(async usr => {
        await group.addUser(usr);
    });
}

项目环境

  • Node版本:v20.2.0
  • npm版本:v9.6.3
  • 依赖列表:
"dependencies": {
    "@azure/identity": "^3.2.2",
    "@azure/web-pubsub": "^1.1.1",
    "@azure/web-pubsub-client": "^1.0.0-beta.3",
    "axios": "0.21.4",
    "core-js": "3.25.3",
    "crypto-browserify": "^3.12.0",
    "msal": "^1.4.18",
    "stream-browserify": "^3.0.0",
    "util": "^0.12.5",
    "uuid": "^9.0.0",
    "vue": "2.6.14",
    "vue-i18n": "8.27.2",
    "vue-router": "3.5.2",
    "vuex": "3.6.2"
},
"devDependencies": {
    "@babel/core": "7.15.5",
    "@babel/eslint-parser": "7.15.7",
    "@babel/preset-env": "7.15.6",
    "@types/jest": "27.4.0",
    "@types/node": "17.0.16",
    "@types/uuid": "^9.0.0",
    "@typescript-eslint/eslint-plugin": "5.11.0",
    "@typescript-eslint/parser": "5.11.0",
    "@vue/eslint-config-typescript": "9.1.0",
    "@vue/test-utils": "1.2.2",
    "@vue/vue2-jest": "27.0.0-alpha.2",
    "babel-loader": "8.2.2",
    "clean-webpack-plugin": "4.0.0",
    "concurrently": "7.4.0",
    "cors": "2.8.5",
    "css-loader": "6.2.0",
    "dotenv-webpack": "7.0.3",
    "eslint": "7.32.0",
    "eslint-plugin-vue": "8.0.1",
    "eslint-webpack-plugin": "3.2.0",
    "html-webpack-plugin": "5.3.2",
    "jest": "27.3.1",
    "jest-fetch-mock": "3.0.3",
    "jest-sonar-reporter": "2.0.0",
    "mini-css-extract-plugin": "2.5.3",
    "nodemon": "2.0.20",
    "postcss-loader": "6.1.1",
    "process": "0.11.10",
    "sass": "1.41.1",
    "sass-loader": "12.1.0",
    "serve": "12.0.1",
    "stylelint": "13.13.1",
    "stylelint-config-sass-guidelines": "8.0.0",
    "terser-webpack-plugin": "5.2.4",
    "ts-jest": "27.1.3",
    "ts-loader": "9.2.6",
    "typescript": "5.1.3",
    "vue-loader": "15.9.8",
    "vue-remove-attributes": "1.0.3",
    "vue-template-compiler": "2.6.14",
    "webpack": "5.74.0",
    "webpack-bundle-analyzer": "4.6.1",
    "webpack-cli": "4.10.0",
    "webpack-dev-server": "4.11.1"
}

解决方案

这个错误的根源是浏览器环境中缺少Node.js原生的KeyObject类型,而jsonwebtoken在签名时需要用它做实例判断。解决步骤如下:

  1. 补充webpack的ProvidePlugin配置:
    在webpack.config.js中添加ProvidePlugin,注入Buffer和process,同时完善fallback和alias配置:
const webpack = require('webpack');

module.exports = {
  // ...其他配置
  plugins: [
    new webpack.ProvidePlugin({
      Buffer: ['buffer', 'Buffer'],
      process: 'process/browser',
    }),
  ],
  resolve: {
    fallback: {
      "crypto": require.resolve("crypto-browserify"),
      "stream": require.resolve("stream-browserify"),
      "buffer": require.resolve("buffer"),
      "util": require.resolve("util/"),
      "child_process": false,
    },
    alias: {
      'crypto': 'crypto-browserify',
    }
  }
};
  1. 调整Azure SDK的使用架构:
    @azure/web-pubsub的服务端SDK(WebPubSubServiceClient)不适合在浏览器环境运行,因为它需要使用密钥生成访问令牌,密钥暴露在前端会有泄露风险。正确做法:
  • 在后端服务部署WebPubSubServiceClient,提供接口供前端调用,返回生成好的客户端访问URL。
  • 前端仅使用@azure/web-pubsub-client连接该URL,完成订阅、发消息等操作。
  1. 依赖版本调整:
    将@azure/web-pubsub-client升级到稳定版(当前最新稳定版为1.0.1),beta版本可能存在浏览器兼容性问题。同时移除前端不需要的@azure/identity依赖(后端使用则保留在后端项目中)。

  2. 密钥安全验证:
    确保前端代码中没有直接暴露Azure Web PubSub的API密钥,所有令牌生成逻辑移到后端,避免密钥泄露风险。


内容的提问来源于stack exchange,提问作者Binod Mahto

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.19 09:10:04