本文将指导你了解如何开始使用 @azure/msal-react。 我们将介绍初始化、判断用户是否已经过身份验证、保护组件、让用户登录并获取访问令牌。
先决条件
初始 化
@azure/msal-react 是基于 React 上下文 API 构建的,需要身份验证的应用的所有部分都必须包装在 MsalProvider 组件中。 首先,您需要初始化PublicClientApplication的一个实例,然后将该实例作为 prop 传递给MsalProvider。
import React from "react";
import { createRoot } from "react-dom/client";
import { MsalProvider } from "@azure/msal-react";
import { Configuration, PublicClientApplication } from "@azure/msal-browser";
import App from "./app.jsx";
// MSAL configuration
const configuration: Configuration = {
auth: {
clientId: "client-id"
}
};
const pca = new PublicClientApplication(configuration);
// Component
const AppProvider = () => (
<MsalProvider instance={pca}>
<App />
</MsalProvider>
);
const root = createRoot(document.getElementById("root"));
root.render(<AppProvider />);
所有MsalProvider下的组件将通过上下文访问PublicClientApplication实例,并且所有由@azure/msal-react提供的挂钩和组件也可供使用。
确定是否对用户进行身份验证
大多数应用程序都需要根据用户是否登录来有条件地呈现某些组件。
@azure/msal-react 提供了 2 种简单的方法来执行此操作。
AuthenticatedTemplate 和 UnauthenticatedTemplate
AuthenticatedTemplate 和 UnauthenticatedTemplate 组件将分别仅在用户通过身份验证或未通过身份验证时渲染其子项。
import React from 'react';
import { AuthenticatedTemplate, UnauthenticatedTemplate } from "@azure/msal-react";
export function App() {
return (
<React.Fragment>
<p>Anyone can see this paragraph.</p>
<AuthenticatedTemplate>
<p>At least one account is signed in!</p>
</AuthenticatedTemplate>
<UnauthenticatedTemplate>
<p>No users are signed in!</p>
</UnauthenticatedTemplate>
</React.Fragment>
);
}
useIsAuthenticated 挂钩
作为上述包装组件的替代方案,你的应用可以使用 useIsAuthenticated 钩子。 可以在 MSAL React 中的 Hooks 中阅读有关此内容的详细信息。
import React from 'react';
import { useIsAuthenticated } from "@azure/msal-react";
export function App() {
const isAuthenticated = useIsAuthenticated();
return (
<React.Fragment>
<p>Anyone can see this paragraph.</p>
{isAuthenticated && (
<p>At least one account is signed in!</p>
)}
{!isAuthenticated && (
<p>No users are signed in!</p>
)}
</React.Fragment>
);
}
保护组件
如果您有一些组件只想向已通过身份验证的用户显示,则可以使用上述任何一种方法。 但是,如果要在用户尚未进行身份验证的情况下自动调用登录名,该怎么办?
@azure/msal-react提供了两种实现此操作的方式:使用MsalAuthenticationTemplate或useMsalAuthentication钩子。
MsalAuthenticationTemplate 组件
MsalAuthenticationTemplate 组件将在用户通过身份验证时渲染其子项,或尝试让用户登录。 只需向其提供要使用的交互类型(重定向或弹出窗口),并可以选择将 请求对象 传递给登录 API、身份验证正在进行时显示的组件或发生错误时要显示的组件。
可以在页面上的任何示例/profile中找到此示例。
import React from "react";
import { MsalAuthenticationTemplate } from "@azure/msal-react";
import { InteractionType } from "@azure/msal-browser";
function ErrorComponent({error}) {
return <p>An Error Occurred: {error}</p>;
}
function LoadingComponent() {
return <p>Authentication in progress...</p>;
}
export function Example() {
const authRequest = {
scopes: ["openid", "profile"]
};
return (
// authenticationRequest, errorComponent and loadingComponent props are optional
<MsalAuthenticationTemplate
interactionType={InteractionType.Popup}
authenticationRequest={authRequest}
errorComponent={ErrorComponent}
loadingComponent={LoadingComponent}
>
<p>At least one account is signed in!</p>
</MsalAuthenticationTemplate>
)
};
useMsalAuthentication 挂钩
useMsalAuthentication 钩子会先检查是否有用户已登录,如果当前没有用户登录,则尝试登录一个用户。 需要提供要使用的交互类型(重定向或弹出窗口)。
它将返回登录操作的结果、发生的任何错误,以及如果需要重试,可以使用的登录函数。
您可以在hooks 文档中了解有关此钩子的更多信息。
import React from 'react';
import { useMsalAuthentication } from "@azure/msal-react";
import { InteractionType } from '@azure/msal-browser';
export function App() {
const {login, result, error} = useMsalAuthentication(InteractionType.Popup);
return (
<React.Fragment>
<p>Anyone can see this paragraph.</p>
<AuthenticatedTemplate>
<p>At least one account is signed in!</p>
</AuthenticatedTemplate>
<UnauthenticatedTemplate>
<p>No users are signed in!</p>
</UnauthenticatedTemplate>
</React.Fragment>
);
}
使用 @azure/msal-browser 提供的登录 API 让用户登录
另一种调用登录的方法是直接使用 PublicClientApplication 实例中的 @azure/msal-browser API。 可通过 3 种方法从上下文访问实例。
useMsal 挂钩
一个钩子,返回 PublicClientApplication 实例、当前登录的所有帐户的数组以及一个告诉你 msal 当前正在执行的 inProgress 值。
你可以在 Hooks 文档中阅读有关此 Hook 的更多内容。
import React from 'react';
import { useMsal } from "@azure/msal-react";
export function App() {
const { instance, accounts, inProgress } = useMsal();
if (accounts.length > 0) {
return <span>There are currently {accounts.length} users signed in!</span>
} else if (inProgress === "login") {
return <span>Login is currently in progress!</span>
} else {
return (
<>
<span>There are currently no users signed in!</span>
<button onClick={() => instance.loginPopup()}>Login</button>
</>
);
}
}
使用原始上下文
如果你使用的是类组件且不能使用钩子,你可以通过 MsalContext 使用原始 msal 上下文。
你可以在此处阅读更多关于在类组件中使用@azure/msal-react的信息。
import React from "react";
import { MsalContext } from "@azure/msal-react";
class App extends React.Component {
static contextType = MsalContext;
render() {
if (this.context.accounts.length > 0) {
return <span>There are currently {this.context.accounts.length} users signed in!</span>
} else if (this.context.inProgress === "login") {
return <span>Login is currently in progress!</span>
} else {
return (
<>
<span>There are currently no users signed in!</span>
<button onClick={() => this.context.instance.loginPopup()}>Login</button>
</>
);
}
}
}
使用 withMsal 高阶组件将你的组件包装起来
在类组件和函数组件中消费 MSAL 上下文的另一种方法是用 withMsal HOC 包装你的组件,这将把上下文注入到组件的 props 中。
import React from "react";
import { withMsal } from "@azure/msal-react";
class LoginButton extends React.Component {
render() {
const isAuthenticated = this.props.msalContext.accounts.length > 0;
const msalInstance = this.props.msalContext.instance;
if (isAuthenticated) {
return <button onClick={() => msalInstance.logout()}>Logout</button>
} else {
return <button onClick={() => msalInstance.loginPopup()}>Login</button>
}
}
}
export default YourWrappedComponent = withMsal(LoginButton);
获取访问令牌
建议您的应用在每次需要使用访问令牌来访问 API 时,都调用您的 PublicClientApplication 对象上的 acquireTokenSilent API。 这可以类似于上一部分所述的方法。
import React, { useState, useEffect } from "react"
import { useMsal, useAccount } from "@azure/msal-react";
export function App() {
const { instance, accounts, inProgress } = useMsal();
const account = useAccount(accounts[0] || {});
const [apiData, setApiData] = useState(null);
useEffect(() => {
if (account) {
instance.acquireTokenSilent({
scopes: ["User.Read"],
account: account
}).then((response) => {
if(response) {
callMsGraph(response.accessToken).then((result) => setApiData(result));
}
});
}
}, [account, instance]);
if (accounts.length > 0) {
return (
<>
<span>There are currently {accounts.length} users signed in!</span>
{apiData && (<span>Data retreived from API: {JSON.stringify(apiData)}</span>)}
</>
);
} else if (inProgress === "login") {
return <span>Login is currently in progress!</span>
} else {
return <span>There are currently no users signed in!</span>
}
}
获取 React 组件外部的访问令牌
如果你需要在 React 组件外部获取访问令牌,可以直接在 PublicClientApplication 上调用 acquireTokenSilent 函数。
我们不建议在 MsalProvider 提供的 React 上下文之外调用会更改用户身份验证状态(登录、登出)的函数,因为该上下文中的组件可能无法正确更新。
请记住,必须先登录用户才能获取令牌。
import { PublicClientApplication } from "@azure/msal-browser";
// This should be the same instance you pass to MsalProvider
const msalInstance = new PublicClientApplication(config);
const acquireAccessToken = async (msalInstance) => {
const activeAccount = msalInstance.getActiveAccount(); // This will only return a non-null value if you have logic somewhere else that calls the setActiveAccount API
const accounts = msalInstance.getAllAccounts();
if (!activeAccount && accounts.length === 0) {
/*
* User is not signed in. Throw error or wait for user to login.
* Do not attempt to log a user in outside of the context of MsalProvider
*/
}
const request = {
scopes: ["User.Read"],
account: activeAccount || accounts[0]
};
const authResult = await msalInstance.acquireTokenSilent(request);
return authResult.accessToken
};
另见
- 有关工作示例,请参阅 我们的 react 路由器示例 。
- 将 MSAL React 与类组件配合使用