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

React Router 6.4.1 Loader特性结合MSAL的最佳实践咨询

React Router 6.4.1 Loader结合MSAL的最佳实现方案

你遇到的核心问题是React Router 6.4+的Loader属于普通函数,无法直接使用MSAL的Hooks获取实例,以下是两种实用的解决方:

方案一:全局导出MSAL实例(简单直接)

在index.tsx中导出已初始化的msalInstance,让Loader可以直接导入使用:

修改index.tsx:

export const msalInstance = new PublicClientApplication(msalConfig);
const accounts = msalInstance.getAllAccounts();

if (accounts.length > 0) {
  msalInstance.setActiveAccount(accounts[0]);
}

msalInstance.addEventCallback((event: EventMessage) => {
  if (event.eventType === EventType.LOGIN_SUCCESS && event.payload) {
    const payload = event.payload as AuthenticationResult;
    const account = payload.account;
    msalInstance.setActiveAccount(account);
  }
});

// 后续渲染代码保持不变

在UserEditScreen.tsx的Loader中导入并使用:

import { msalInstance } from '../index';
import { InteractionRequiredAuthError } from '@azure/msal-browser';

export const loader = async ({ request, params }: LoaderFunctionArgs) => {
  const account = msalInstance.getActiveAccount();
  
  // 未登录时直接重定向到登录页
  if (!account) {
    return redirect('/login');
  }

  try {
    // 静默获取访问令牌
    const tokenResponse = await msalInstance.acquireTokenSilent({
      account,
      scopes: ['你的Azure API权限范围'] // 替换为实际权限
    });

    // 调用受保护的Azure端点
    const userResponse = await fetch(`https://your-azure-api.com/users/${params.id}`, {
      headers: {
        Authorization: `Bearer ${tokenResponse.accessToken}`
      }
    });

    if (!userResponse.ok) {
      throw new Error('获取用户数据失败');
    }

    return userResponse.json();
  } catch (error) {
    // 处理需要交互式登录的错误
    if (error instanceof InteractionRequiredAuthError) {
      msalInstance.acquireTokenRedirect({
        account,
        scopes: ['你的Azure API权限范围']
      });
    }
    // 抛出错误交由React Router的errorElement处理
    throw error;
  }
};

方案二:在App组件内创建Router,注入MSAL实例(更符合组件封装)

将Router的创建移到App组件内部,通过高阶函数给Loader注入MSAL实例,避免全局变量:

修改app.tsx:

import { createBrowserRouter, RouterProvider, createRoutesFromElements, Route } from 'react-router-dom';
import { PublicClientApplication } from '@azure/msal-browser';
import { MsalProvider } from '@azure/msal-react';
import GlobalError from './GlobalError';
import AuthenticatedScreen from './AuthenticatedScreen';
import AdministrationScreen from './AdministrationScreen';
import UsersScreen from './UsersScreen';
import UserNewScreen from './UserNewScreen';
import UserEditScreen from './UserEditScreen';
import UserEditError from './UserEditError';
import { userLoader } from './UserEditScreen';

type AppProps = {
  msalInstance: PublicClientApplication;
};

function App({ msalInstance }: AppProps) {
  // 在组件内部创建Router,直接访问msalInstance
  const router = createBrowserRouter(
    createRoutesFromElements(
      <Route path="/" errorElement={<GlobalError />}>
        <Route path="/administration" element={
          <AuthenticatedScreen>
            <AdministrationScreen />
          </AuthenticatedScreen>
        }>
          <Route path='users' element={<UsersScreen />} />
          <Route path='users/new' element={<UserNewScreen />} />
          <Route 
            path='users/:id/edit'
            element={<UserEditScreen />}
            // 包装Loader,传入msalInstance
            loader={(args) => userLoader(args, msalInstance)}
            errorElement={<UserEditError />}
          />
        </Route>
      </Route>
    )
  );

  return (
    <MsalProvider instance={msalInstance}>
      <RouterProvider router={router} />
    </MsalProvider>
  );
}

export default App;

修改UserEditScreen.tsx的Loader,接受MSAL实例参数:

import { LoaderFunctionArgs } from 'react-router-dom';
import { PublicClientApplication, InteractionRequiredAuthError } from '@azure/msal-browser';

export const userLoader = async (
  { request, params }: LoaderFunctionArgs,
  msalInstance: PublicClientApplication
) => {
  const account = msalInstance.getActiveAccount();
  
  if (!account) {
    return redirect('/login');
  }

  try {
    const tokenResponse = await msalInstance.acquireTokenSilent({
      account,
      scopes: ['你的Azure API权限范围']
    });

    const userResponse = await fetch(`https://your-azure-api.com/users/${params.id}`, {
      headers: {
        Authorization: `Bearer ${tokenResponse.accessToken}`
      }
    });

    if (!userResponse.ok) {
      throw new Error('获取用户数据失败');
    }

    return userResponse.json();
  } catch (error) {
    if (error instanceof InteractionRequiredAuthError) {
      msalInstance.acquireTokenRedirect({
        account,
        scopes: ['你的Azure API权限范围']
      });
    }
    throw error;
  }
};

关键注意事项

  • 始终检查getActiveAccount()的返回值,确保用户已登录,未登录时直接重定向到登录页
  • 捕获InteractionRequiredAuthError,这种情况需要触发交互式登录(重定向或弹窗)
  • 确保MSAL配置中已正确设置Azure API的权限范围和端点

内容的提问来源于stack exchange,提问作者Alex Alvarez Gárciga

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.18 05:55:26