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 对象,其中包含 interactionType、protectedResourceMap 以及可选的 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导入,并设置为Popup或Redirect。

{
    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.com 但不a.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 中以保护所有路由。 请改用通配符匹配。

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