我正在尝试将 PowerBI 报告嵌入到 React 应用程序中。我遵循了微软的这个例子。该示例位于
Typescript
但我正在使用 Javascript
这是我的
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";
// TRIED THIS TO FIX THE ISSUE
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 // TRIED THIS TO FIX THE ISSUE
}
}
});
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>
);
};
当我在端口 3000 上本地运行该项目时,我没有收到任何错误。但是当我部署到服务器时,我收到以下错误
Uncaught BrowserAuthError: crypto_nonexistent: The crypto object or function is not available.
该项目是使用
CRA
创建的。怎么解决这个问题?
为 Windows Server 上的 IIS 上托管的 React 应用程序启用 HTTPS。您需要 SSL 证书才能启用 HTTPS。
创建用于内部测试的自签名证书。使用 IIS 管理器创建或导入证书。
打开
IIS Manager
双击服务器证书。
在操作面板中,单击创建自签名证书。
为其命名(例如,
MyTestCert
),它将出现在列表中。
创建 SSL 证书后 将证书绑定到站点。
在右侧面板中,单击“绑定”。
添加 https 绑定并选择您的 SSL 证书。
选择
HTTPS
。
MSAL 身份验证建议使用 HTTPS,特别是对于生产应用程序。 MSAL 尝试使用
crypto
API 执行身份验证任务(例如生成令牌)。您可以使用自定义加密选项配置 MSAL,以防止在 crypto
不可用时出现错误。