MSAL 侦听器

MSAL Angular 向已知的受保护资源提供一个 Interceptor 类,该类可自动为使用 Angular http 客户端的传出请求获取令牌。 本文档提供有关配置和使用 MsalInterceptor的详细信息。

虽然我们建议使用 MsalInterceptor,而不是直接使用 acquireTokenSilent API,但请注意,是否使用 MsalInterceptor 是可选的。 你可能希望改为使用 acquireToken API 显式获取令牌。

请注意,为方便起见提供, MsalInterceptor 可能不适合所有用例。 如果你有 MsalInterceptor 无法满足的特定需求,我们建议你编写自己的拦截器。

Configuration

app.module.ts 中配置 MsalInterceptor

可以在 app.module.ts 中将 MsalInterceptor 连同其配置一起添加到应用程序中,作为提供程序。 这些导入项引入了一个 MSAL 实例,以及两个 Angular 专用的配置对象。 第三个参数是一个 MsalInterceptorConfiguration 对象,其中包含 interactionTypeprotectedResourceMap 以及可选的 authRequest 的值。

配置可能如下所示。 有关为应用配置 MSAL Angular 的其他方法,请参阅我们的 配置文档

import { NgModule } from '@angular/core';
import { HTTP_INTERCEPTORS, HttpClientModule } from "@angular/common/http";
import { AppComponent } from './app.component';
import { MsalModule, MsalRedirectComponent, MsalGuard, MsalInterceptor } from '@azure/msal-angular'; // Import MsalInterceptor
import { InteractionType, PublicClientApplication } from '@azure/msal-browser';

@NgModule({
    declarations: [
        AppComponent,
    ],
    imports: [
        MsalModule.forRoot( new PublicClientApplication({
            // MSAL Configuration
        }), {
            // MSAL Guard Configuration
        }, {
            // MSAL Interceptor Configurations
            interactionType: InteractionType.Redirect,
            protectedResourceMap: new Map([ 
                ['Enter_the_Graph_Endpoint_Here/v1.0/me', ['user.read']]
            ])
        })
    ],
    providers: [
        {
            provide: HTTP_INTERCEPTORS, // Provides as HTTP Interceptor
            useClass: MsalInterceptor,
            multi: true
        },
        MsalGuard
    ],
    bootstrap: [AppComponent, MsalRedirectComponent]
})
export class AppModule { }

交互类型

MsalInterceptor虽然设计为以无提示方式获取令牌,但当无提示请求失败时,它将回退到以交互方式获取令牌。 InteractionType可从@azure/msal-browser导入,并设置为PopupRedirect

{
    interactionType: InteractionType.Redirect,
    protectedResourceMap: new Map([ 
        ['Enter_the_Graph_Endpoint_Here/v1.0/me', ['user.read']]
    ])
}

受保护的资源映射

受保护的资源及其对应范围在 MsalInterceptor 配置中以 protectedResourceMap 的形式提供。

protectedResourceMap 集合中输入的 URL 区分大小写。 对于每个资源,添加所请求的、需要在访问令牌中返回的范围。

例如:

  • ["user.read"]适用于 Microsoft Graph
  • ["<Application ID URL>/scope"] 用于自定义 Web API(即 api://<Application ID>/access_as_user

可以通过以下方式为资源指定范围:

  1. 一组作用域,无论使用何种 HTTP 方法,这些作用域都会被添加到针对该资源的每个 HTTP 请求中。
{
    interactionType: InteractionType.Redirect,
    protectedResourceMap: new Map<string, Array<string> | null>([
        ["https://graph.microsoft.com/v1.0/me", ["user.read", "profile"]],
        ["https://myapplication.com/user/*", ["customscope.read"]]
    ]),
}
  1. 一个 ProtectedResourceScopes 数组,它将仅为特定的 HTTP 方法附加作用域。
{
    interactionType: InteractionType.Redirect,
    protectedResourceMap: new Map<string, Array<string|ProtectedResourceScopes> | null>([
        ["https://graph.microsoft.com/v1.0/me", ["user.read"]],
        ["http://myapplication.com", [
            {
                httpMethod: "POST",
                scopes: ["write.scope"]
            }
        ]]
    ])
}

请注意,资源的范围可以包含字符串和 ProtectedResourceScopes。 在下面的示例中, GET 请求将具有范围 "all.scope""read.scope"而请求 PUT 将只具有 "all.scope"

{
    interactionType: InteractionType.Redirect,
    protectedResourceMap: new Map<string, Array<string|ProtectedResourceScopes> | null>([
        ["http://myapplication.com", [
            "all.scope",
            {
                httpMethod: "GET",
                scopes: ["read.scope"]
            },
            {
                httpMethod: "POST",
                scopes: ["info.scope"]
            }
        ]]
    ])
}
  1. 范围值 null,指示资源不受保护且不会获取令牌。 未包含在 protectedResourceMap 中的资源默认不受保护。 当某个资源中的部分路由需要保护,而部分路由不需要保护时,将特定资源设为不受保护会很有用。 请注意,protectedResourceMap 中的顺序很重要,因此应将 null 资源放在任何类似的基础 URL 或通配符之前。
{
    interactionType: InteractionType.Redirect,
    protectedResourceMap: new Map<string, Array<string> | null>([
        ["https://graph.microsoft.com/v1.0/me", ["user.read", "profile"]],
        ["https://myapplication.com/unprotected", null],
        ["https://myapplication.com/unprotected/post", [{ httpMethod: 'POST', scopes: null }]],
        ["https://myapplication.com", ["custom.scope"]]
    ]),
}

关于 protectedResourceMap,还需注意以下几点:

  • 通配符protectedResourceMap 支持使用 * 作为通配符。 使用通配符时,如果在 protectedResourceMap 中找到多个匹配项,将使用找到的第一个匹配项(具体取决于 protectedResourceMap 的顺序)。
  • 相对路径:如果应用程序中存在相对资源路径,则可能需要提供应用程序中 protectedResourceMap的相对路径。 这也适用于 ngx-translate 可能出现的问题。 请注意,根据你的应用程序不同,protectedResourceMap 中的相对路径可能需要开头加斜杠,也可能不需要,因此可能需要两种写法都试一试。

严格匹配(strictMatching

在 msal-angular v5 中,条目的 protectedResourceMap URL 组件模式匹配默认使用严格的匹配语义。 MsalInterceptorConfiguration 上的 strictMatching 字段控制此行为。

Important

如果应用程序动态设置 protectedResourceMap 键(例如,来自环境文件、APP_INITIALIZER 或 JSON 配置),并且这些键是不包含子路径或通配符的基础 URL,那么严格匹配可能会在无提示的情况下导致无法附加 Authorization 标头。 这会导致出现 401 错误,但不会在构建时出错,也不会针对每个请求发出警告;只有在未显式配置 strictMatching 的情况下,才会出现一次性的初始化警告。 有关详细信息,请参阅 故障排除严格匹配

严格匹配会带来哪些变化

Behavior 旧版 (strictMatching: false) 严格(v5 中的默认值)
元字符转义 . 和其他正则表达式元字符不会被转义;它们会作为正则表达式运算符起作用 所有元字符(包括 .)都被视为 文本
定位 模式可能与字符串中的任何位置匹配 模式必须与 完整字符串 匹配 (^…$
主机通配符 (* * 匹配任何字符序列,包括 . * 匹配任何包含 . 的任何字符序列(通配符仅限于单个 DNS 标签内)
路径/搜索/哈希通配符 (* * 匹配任何字符序列 * 匹配任何字符序列(未更改)
? 字符 传递给基础正则表达式 被视为文本?(URL 查询字符串分隔符,而不是通配符)

使用严格匹配(v5 默认值):

  • 匹配 app.contoso.coma.b.contoso.com匹配(通配符不能跨越点分隔符)的类似于 *.contoso.com 的模式。
  • 形如 https://graph.microsoft.com/v1.0/me 的模式仅匹配该确切 URL。

常见故障模式

以下 protectedResourceMap 键模式在旧版匹配下可以正常工作,但在严格匹配下会静默失败:

密钥模式 传出请求 URL 严格匹配下的结果 修复
https://api.example.com https://api.example.com/v1/users 不匹配 — 键解析为路径 /,请求具有路径 /v1/users https://api.example.com/*
https://api.example.com/ https://api.example.com/v1/users 无匹配 - 尾部反斜杠将模式固定为精确/ https://api.example.com/*
environment.apiConfig.uri(例如,https://api.example.com https://api.example.com/v1/users 不匹配 - 与上面相同 `${environment.apiConfig.uri}/*`

v5 中的默认行为(无需配置)

默认情况下启用严格匹配。 不需要其他配置:

{
    interactionType: InteractionType.Redirect,
    protectedResourceMap: new Map([
        ["https://*.contoso.com/api", ["contoso.scope"]],
        ["https://graph.microsoft.com/v1.0/me", ["user.read"]]
    ])
    // strictMatching defaults to true in v5
}

选择不使用旧版匹配

如果您的模式依赖于 v4 中较宽松的匹配方式,您可以设置 strictMatching: false 以暂时保留旧版行为:

注释

为确保向后兼容性,提供了旧版匹配功能 (strictMatching: false),该功能可能会在未来的主要版本中被移除。 我们建议你更新 protectedResourceMap 模式,使其支持严格匹配。

{
    interactionType: InteractionType.Redirect,
    protectedResourceMap: new Map([
        ["https://*.contoso.com/api", ["contoso.scope"]],
        ["https://graph.microsoft.com/v1.0/me", ["user.read"]]
    ]),
    strictMatching: false  // Use legacy matching for backwards compatibility
}

环境驱动配置指南

如果您的 protectedResourceMap 键引用 Angular environment 值(例如 environment.apiConfig.uri),请检查这些值是精确路径(例如 https://graph.microsoft.com/v1.0/me)还是纯基 URL(例如 https://api.example.com)。 精确路径在严格匹配下也能正常工作,不需要特殊处理:

export function MSALInterceptorConfigFactory(): MsalInterceptorConfiguration {
  const protectedResourceMap = new Map<string, Array<string>>();
  // environment.apiConfig.uri is an exact path (e.g. "https://graph.microsoft.com/v1.0/me")
  // — strict matching works correctly
  protectedResourceMap.set(environment.apiConfig.uri, environment.apiConfig.scopes);

  return {
    interactionType: InteractionType.Redirect,
    protectedResourceMap,
  };
}

如果环境值是裸基 URL,并且需要匹配子路径,请追加 /* 通配符:

  // environment.apiConfig.uri is a base URL (e.g. "https://api.example.com")
  // Append /* to match all sub-paths
  protectedResourceMap.set(`${environment.apiConfig.uri}/*`, environment.apiConfig.scopes);

对于那些在构建时无法确定键形状的真正动态配置(例如 APP_INITIALIZER、通过 fetch 加载的 JSON 或 platformBrowserDynamic),请将 strictMatching: false 设为临时的安全默认值。 有关代码示例 ,请参阅“修复”选项 - 选项 B

严格匹配疑难解答

症状
  • API 请求在升级到 v5 后返回 @azure/msal-angular(或在 v5 次要版本(如 5.0.x → 5.1.x)之间)。
  • 传出的 HTTP 请求中缺少Authorization: Bearer <token>标头。
  • 未报告生成时或运行时错误 - 失败为 无提示
  • 该问题可能只会在某些环境(例如预发布/生产)中出现,在这些环境中,API 基础 URL 与开发环境不同。
修复选项

选项 A:更新密钥以使用严格匹配(建议)

更新您的 protectedResourceMap 密钥,以使用符合严格匹配规则的精确路径或通配符。 此方法是首选方法,因为严格匹配更安全且更具可预测性:

{
    interactionType: InteractionType.Redirect,
    protectedResourceMap: new Map([
        // Exact path — matches only this URL
        ["https://graph.microsoft.com/v1.0/me", ["user.read"]],
        // Wildcard — matches all sub-paths of the API
        ["https://api.example.com/v1/*", ["api.scope"]]
    ])
    // strictMatching defaults to true — no need to set it
}

选项 B:设置 strictMatching: false(用于动态配置的后备方案)

protectedResourceMap如果密钥在运行时动态加载(例如,来自APP_INITIALIZER、JSON 配置或platformBrowserDynamic),并且无法保证它们包含确切的路径或通配符,则设置为strictMatching: false临时安全默认值:

{
    interactionType: InteractionType.Redirect,
    protectedResourceMap: new Map([
        [config.apiUri, config.apiScopes]
    ]),
    // Dynamic keys may be base URLs without wildcards.
    // Remove once keys are migrated to exact paths or wildcard patterns.
    strictMatching: false
}

注释

为确保向后兼容性,提供了旧版匹配功能 (strictMatching: false),该功能可能会在未来的主要版本中被移除。

运行时警告

strictMatching 未被显式配置时,在初始化过程中,MsalInterceptor 会通过 MSAL 记录器发出一次性运行时警告。 如果看到此警告,请按照上述修复选项进行操作。

可选 authRequest

有关可在 MsalInterceptorConfiguration 中设置的可选 authRequest 的更多信息,请参阅我们的多租户文档

从 msal-angular v1 到 v2 的变更

注释

MSAL Angular v1 的 MsalAngularConfiguration 中的 unprotectedResourceMap 已弃用,且不再有效。

  • protectedResourceMap 已移至 MsalInterceptorConfiguration 对象,并且可以作为 Map<string, Array<string|ProtectedResourceScopes>> 传递。 MsalAngularConfiguration 已弃用,不再有效。
  • 不再支持将根域放在 protectedResourceMap 中以保护所有路由。 请改用通配符匹配。

有关如何配置范围的详细信息,请参阅 我们的常见问题解答