React应用集成PowerBI部署后出现crypto_nonexistent错误的解决方法咨询
React应用集成PowerBI部署后出现crypto_nonexistent错误的解决方法咨询
大家好,我最近在做把PowerBI报表嵌入React应用的需求,参考了微软官方的一个TypeScript示例来实现,但我的项目是纯JavaScript环境。本地在3000端口运行时完全没有问题,但部署到服务器之后,就遇到了这个报错:
Uncaught BrowserAuthError: crypto_nonexistent: The crypto object or function is not available.
我已经尝试了两个修复方案:一是在代码开头添加window.crypto的兼容处理,二是在MSAL配置里把usePkce设为false,但都没能解决问题。
下面是我的关键代码,麻烦大家帮我看看哪里出了问题:
项目入口index.js
import React from "react"; import ReactDOM from "react-dom/client"; import "./index.css"; import App from "./App"; import { BrowserRouter } from "react-router-dom"; import { AuthProvider } from "./context/AuthProvider"; import { CountryProvider } from "./context/CountryProvider"; import theme from "./theme"; import { ThemeProvider } from "@mui/material/styles"; import { Provider } from "react-redux"; import { store } from "./store/store"; import * as config from "./pages/powerbi/keys"; import { LogLevel, PublicClientApplication } from "@azure/msal-browser"; import { MsalProvider } from "@azure/msal-react"; // 尝试过的修复代码 if (typeof window !== 'undefined' && !window.crypto) { window.crypto = window.msCrypto; } const msalInstance = new PublicClientApplication({ auth: { clientId: config.clientId, authority: config.authorityUrl, redirectUri: "http://{SERVER_IP_ADDRESS}/FOLDER_NAME/" }, cache: { cacheLocation: 'localStorage', storeAuthStateInCookie: false, }, system: { loggerOptions: { loggerCallback: (level, message, containsPii) => { console.log(`LEVEL: ${level} MESSAGE: ${message}`); }, piiLoggingEnabled: false, logLevel: LogLevel.Verbose }, cryptoOptions: { usePkce: false // 尝试过的修复 } } }); console.log("CRYPTO: ", window.crypto); const root = ReactDOM.createRoot(document.getElementById("root")); root.render( <div id="MainDiv"> <Provider store={store}> <BrowserRouter basename="Reactjs_Demo/"> <AuthProvider> <CountryProvider> <ThemeProvider theme={theme}> <MsalProvider instance={msalInstance}> <App /> </MsalProvider> </ThemeProvider> </CountryProvider> </AuthProvider> </BrowserRouter> </Provider> </div> );
PowerBI嵌入组件代码
import React, { useEffect, useRef, useState, useContext } from "react"; import { AuthenticationResult, InteractionType, EventType, AuthError } from "@azure/msal-browser"; import { MsalContext } from "@azure/msal-react"; import { service, factories, models } from "powerbi-client"; import * as config from "./keys"; const powerbi = new service.Service(factories.hpmFactory, factories.wpmpFactory, factories.routerFactory); export default function PowerBiReport() { const [accessToken, setAccessToken] = useState(""); const [embedUrl, setEmbedUrl] = useState(""); const [error, setError] = useState([]); const reportRef = useRef(null); const context = useContext(MsalContext); const authenticate = () => { const msalInstance = context.instance; const msalAccounts = context.accounts; const msalInProgress = context.inProgress; const isAuthenticated = context.accounts.length > 0; if (error.length > 0) { return; } const eventCallback = msalInstance.addEventCallback((message) => { if (message.eventType === EventType.LOGIN_SUCCESS && !accessToken) { const payload = message.payload; const name = payload.account?.name ? payload.account?.name : ""; setAccessToken(payload.accessToken); setUsername(name); tryRefreshUserPermissions(); } }); const loginRequest = { scopes: config.scopeBase, account: msalAccounts[0] }; if (!isAuthenticated && msalInProgress === InteractionType.None) { msalInstance.loginRedirect(loginRequest); } else if (isAuthenticated && accessToken && !embedUrl) { getEmbedUrl(); msalInstance.removeEventCallback(eventCallback); } else if (isAuthenticated && !accessToken && !embedUrl && msalInProgress === InteractionType.None) { setUsername(msalAccounts[0].name); msalInstance.acquireTokenSilent(loginRequest).then((response) => { setAccessToken(response.accessToken); getEmbedUrl(); }).catch((error) => { if (error.errorCode === "consent_required" || error.errorCode === "interaction_required" || error.errorCode === "login_required") { msalInstance.acquireTokenRedirect(loginRequest); } else if (error.errorCode === '429') { setError(["Our Service Token Server (STS) is overloaded, please try again in sometime"]); } else { setError(["There was some problem fetching the access token" + error.toString()]); } }); } }; const tryRefreshUserPermissions = () => { fetch(config.powerBiApiUrl + "v1.0/myorg/RefreshUserPermissions", { headers: { "Authorization": "Bearer " + accessToken }, method: "POST" }) .then(response => { if (response.ok) { console.log("User permissions refreshed successfully."); } else { if (response.status === 429) { console.error("Permissions refresh will be available in up to an hour."); } else { console.error(response); } } }) .catch(error => { console.error("Failure in making API call." + error); }); }; const getEmbedUrl = () => { fetch(config.powerBiApiUrl + "v1.0/myorg/groups/" + config.wspaceId + "/reports/" + config.reportId, { headers: { "Authorization": "Bearer " + accessToken }, method: "GET" }) .then(response => { const errorMessage = ["Error occurred while fetching the embed URL of the report", "Request Id: " + response.headers.get("requestId")]; response.json() .then(body => { if (response.ok) { setEmbedUrl(body["embedUrl"]); } else { errorMessage.push("Error " + response.status + ": " + body.error.code); setError(errorMessage); } }) .catch(() => { errorMessage.push("Error " + response.status + ": An error has occurred"); setError(errorMessage); }); }) .catch(error => { setError([error]); }); }; const setUsername = (username) => { const welcome = document.getElementById("welcome"); if (welcome !== null) welcome.innerText = "Welcome, " + username; }; useEffect(() => { if (reportRef.current !== null) { const reportContainer = reportRef.current; if (error.length) { reportContainer.textContent = ""; error.forEach(line => { reportContainer.appendChild(document.createTextNode(line)); reportContainer.appendChild(document.createElement("br")); }); } else if (accessToken !== "" && embedUrl !== "") { const embedConfiguration = { type: "report", tokenType: models.TokenType.Aad, accessToken, embedUrl, id: config.reportId, }; const report = powerbi.embed(reportContainer, embedConfiguration); report.off("loaded"); report.on("loaded", () => { console.log("Report load successful"); }); report.off("rendered"); report.on("rendered", () => { console.log("Report render successful"); }); report.off("error"); report.on("error", (event) => { const errorMsg = event.detail; console.error(errorMsg); }); } } }, [accessToken, embedUrl, error]); useEffect(() => { authenticate(); }, []); useEffect(() => { return () => { if (reportRef.current) { powerbi.reset(reportRef.current); } }; }, []); return ( <div> <div id="welcome"></div> <div ref={reportRef}> Loading the report... </div> </div> ); };
我的项目是用CRA(Create React App)创建的,有没有大佬能帮我分析下这个问题怎么解决呀?
备注:内容来源于stack exchange,提问作者Mahmud Mridul
相关产品推荐
相关产品推荐

