可以通过多种方式配置 MSAL for Angular。 本文介绍 MSAL Angular 的可用配置选项,包括静态和动态方法,并提供用于将身份验证集成到应用中的代码示例。 使用本指南选择最符合应用程序要求的配置方法,并为用户提供无缝登录体验。
配置选项
@azure/msal-angular 接受三个配置对象:
-
配置:这是用于核心
@azure/msal-browser库的相同配置对象。 可在 此处找到所有配置选项。 -
MsalGuardConfiguration:一组专门用于 Angular 防护的选项。 -
MsalInterceptorConfiguration:一组专门针对 Angular 拦截器的选项。
Angular 专用配置
- 必须在
MsalGuardConfiguration和MsalInterceptorConfiguration上指定interactionType,并且可将其设置为Popup或Redirect。 -
MsalInterceptorConfiguration上的protectedResourceMap对象用于保护路由。 - 可以在
MsalGuardConfiguration和MsalInterceptorConfiguration上指定一个可选的authRequest对象,以设置附加选项。 - 可以在
MsalGuardConfiguration上设置可选的loginFailedRoute字符串。 如果需要登录并失败,Msal Guard 将重定向到此路由。
有关 MSAL Angular v1 的配置、用法和差异的详细信息,请参阅 MsalInterceptor 和 MsalGuard 文档。
重定向的配置
如果您打算使用重定向,我们建议导入 MsalRedirectComponent 并使用 AppComponent 进行引导启动。 有关更多详细信息,请参阅 重定向文档 。
注意: 从 MSAL v3.x 开始,现在需要初始化应用程序对象。 有关更多详细信息,请参阅 v2-v3 升级指南 。 有关升级到 MSAL Angular v5 的详细信息,请参阅 v4-v5 升级指南。
MsalModule.forRoot
该 MsalModule 类包含一个可在 app.module.ts 文件中调用的静态方法:
import { NgModule } from "@angular/core";
import { HTTP_INTERCEPTORS } from "@angular/common/http";
import { AppComponent } from "./app.component";
import { MsalModule, MsalService, MsalGuard, MsalInterceptor, MsalBroadcastService, MsalRedirectComponent } from "@azure/msal-angular";
import { PublicClientApplication, InteractionType, BrowserCacheLocation } from "@azure/msal-browser";
@NgModule({
imports: [
MsalModule.forRoot(
new PublicClientApplication({
// MSAL Configuration
auth: {
clientId: "clientid",
authority: "https://login.microsoftonline.com/common/",
redirectUri: "http://localhost:4200/",
postLogoutRedirectUri: "http://localhost:4200/",
},
cache: {
cacheLocation: BrowserCacheLocation.LocalStorage,
},
system: {
loggerOptions: {
loggerCallback: () => {},
piiLoggingEnabled: false,
},
},
}),
{
interactionType: InteractionType.Popup, // MSAL Guard Configuration
authRequest: {
scopes: ["user.read"],
},
loginFailedRoute: "/login-failed",
},
{
interactionType: InteractionType.Redirect, // MSAL Interceptor Configuration
protectedResourceMap,
}
),
],
providers: [
{
provide: HTTP_INTERCEPTORS,
useClass: MsalInterceptor,
multi: true,
},
MsalGuard,
],
bootstrap: [AppComponent, MsalRedirectComponent],
})
export class AppModule {}
工厂提供程序
还可以通过工厂提供程序提供配置选项。
import { MsalModule, MsalService, MsalInterceptor, MsalInterceptorConfiguration, MsalGuard, MsalGuardConfiguration, MsalBroadcastService, MsalRedirectComponent } from "@azure/msal-angular";
import { IPublicClientApplication, PublicClientApplication, InteractionType, BrowserCacheLocation } from "@azure/msal-browser";
export function MSALInstanceFactory(): IPublicClientApplication {
return new PublicClientApplication({
auth: {
clientId: "00001111-aaaa-2222-bbbb-3333cccc4444",
redirectUri: "http://localhost:4200",
postLogoutRedirectUri: "http://localhost:4200",
},
cache: {
cacheLocation: BrowserCacheLocation.LocalStorage,
},
});
}
export function MSALInterceptorConfigFactory(): MsalInterceptorConfiguration {
const protectedResourceMap = new Map<string, Array<string>>();
protectedResourceMap.set("https://graph.microsoft.com/v1.0/me", ["user.read"]);
return {
interactionType: InteractionType.Redirect,
protectedResourceMap,
};
}
export function MSALGuardConfigFactory(): MsalGuardConfiguration {
return {
interactionType: InteractionType.Redirect,
authRequest: {
scopes: ["user.read"],
},
loginFailedRoute: "./login-failed",
};
}
@NgModule({
imports: [MsalModule],
providers: [
{
provide: HTTP_INTERCEPTORS,
useClass: MsalInterceptor,
multi: true,
},
{
provide: MSAL_INSTANCE,
useFactory: MSALInstanceFactory,
},
{
provide: MSAL_GUARD_CONFIG,
useFactory: MSALGuardConfigFactory,
},
{
provide: MSAL_INTERCEPTOR_CONFIG,
useFactory: MSALInterceptorConfigFactory,
},
MsalGuard,
MsalBroadcastService,
MsalService,
],
bootstrap: [AppComponent, MsalRedirectComponent],
})
export class AppModule {}
platformBrowserDynamic
如果需要动态配置 MSAL Angular(例如,基于从 API 返回的值),可以使用 platformBrowserDynamic。
platformBrowserDynamic 是一个平台工厂,用于引导应用程序启动,并且可接受配置选项。
platformBrowserDynamic 设置 Angular 应用程序时应已存在。
以下示例说明了如何使用 platformBrowserDynamic 和一个 JSON 文件动态配置 @azure/msal-angular:
app.module.ts
import { MsalModule, MsalInterceptor, MsalService } from "@azure/msal-angular";
@NgModule({
imports: [MsalModule],
providers: [
{
provide: HTTP_INTERCEPTORS,
useClass: MsalInterceptor,
multi: true,
},
MsalService,
],
bootstrap: [AppComponent],
})
export class AppModule {}
main.ts
import { enableProdMode } from "@angular/core";
import { platformBrowserDynamic } from "@angular/platform-browser-dynamic";
import { AppModule } from "./app/app.module";
import { environment } from "./environments/environment";
import { MSAL_INSTANCE, MSAL_GUARD_CONFIG, MSAL_INTERCEPTOR_CONFIG } from "@azure/msal-angular";
import { PublicClientApplication, Configuration } from "@azure/msal-browser";
if (environment.production) {
enableProdMode();
}
function loggerCallback(logLevel: LogLevel, message: string) {
console.log("MSAL Angular: ", message);
}
fetch("/assets/configuration.json")
.then((response) => response.json())
.then((json) => {
platformBrowserDynamic([
{
provide: MSAL_INSTANCE,
useValue: new PublicClientApplication({
auth: json.msal.auth,
cache: json.msal.cache,
system: {
loggerOptions: {
loggerCallback,
logLevel: LogLevel.Info,
piiLoggingEnabled: false,
},
},
}),
},
{
provide: MSAL_GUARD_CONFIG,
useValue: {
interactionType: json.guard.interactionType,
authRequest: json.guard.authRequest,
loginFailedRoute: json.guard.loginFailedRoute,
} as MsalGuardConfiguration,
},
{
provide: MSAL_INTERCEPTOR_CONFIG,
useValue: {
interactionType: json.interceptor.interactionType,
protectedResourceMap: new Map(json.interceptor.protectedResourceMap),
} as MsalInterceptorConfiguration,
},
])
.bootstrapModule(AppModule)
.catch((err) => console.error(err));
});
src/assets/configuration.json
{
"msal": {
"auth": {
"clientId": "clientid",
"authority": "https://login.microsoftonline.com/common/",
"redirectUri": "http://localhost:4200/",
"postLogoutRedirectUri": "http://localhost:4200/",
"navigateToLoginRequestUrl": true
},
"cache": {
"cacheLocation": "localStorage",
"storeAuthStateInCookie": true
}
},
"guard": {
"interactionType": "redirect",
"authRequest": {
"scopes": ["user.read"]
},
"loginFailedRoute": "/login-failed"
},
"interceptor": {
"interactionType": "redirect",
"protectedResourceMap": [["https://graph.microsoft.com/v1.0/me", ["user.read"]]]
}
}
借助工厂提供程序和 APP_INITIALIZER 实现动态配置
要动态配置 MSAL Angular,你可以使用带有 APP_INITIALIZER 的工厂提供程序。
src/app/config.service.ts
import { Injectable } from "@angular/core";
import { HttpClient, HttpBackend } from "@angular/common/http";
import { map } from "rxjs/operators";
@Injectable({
providedIn: "root",
})
export class ConfigService {
private settings: any;
private http: HttpClient;
constructor(private readonly httpHandler: HttpBackend) {
this.http = new HttpClient(httpHandler);
}
init(endpoint: string): Promise<boolean> {
return new Promise<boolean>((resolve, reject) => {
this.http
.get(endpoint)
.pipe(map((result) => result))
.subscribe(
(value) => {
this.settings = value;
resolve(true);
},
(error) => {
reject(error);
}
);
});
}
getSettings(key?: string | Array<string>): any {
if (!key || (Array.isArray(key) && !key[0])) {
return this.settings;
}
if (!Array.isArray(key)) {
key = key.split(".");
}
let result = key.reduce((account: any, current: string) => account && account[current], this.settings);
return result;
}
}
src/app/msal-config-dynamic.module.ts
import { InjectionToken, NgModule, APP_INITIALIZER } from "@angular/core";
import { IPublicClientApplication, PublicClientApplication, LogLevel } from "@azure/msal-browser";
import { MsalGuard, MsalInterceptor, MsalBroadcastService, MsalInterceptorConfiguration, MsalModule, MsalService, MSAL_GUARD_CONFIG, MSAL_INSTANCE, MSAL_INTERCEPTOR_CONFIG, MsalGuardConfiguration } from "@azure/msal-angular";
import { HTTP_INTERCEPTORS } from "@angular/common/http";
import { ConfigService } from "./config.service";
const AUTH_CONFIG_URL_TOKEN = new InjectionToken<string>("AUTH_CONFIG_URL");
export function initializerFactory(env: ConfigService, configUrl: string): any {
const promise = env.init(configUrl).then((value) => {
console.log("finished getting configurations dynamically.");
});
return () => promise;
}
export function loggerCallback(logLevel: LogLevel, message: string) {
console.log(message);
}
export function MSALInstanceFactory(config: ConfigService): IPublicClientApplication {
return new PublicClientApplication({
auth: config.getSettings("msal").auth,
cache: config.getSettings("msal").cache,
system: {
loggerOptions: {
loggerCallback,
logLevel: LogLevel.Info,
piiLoggingEnabled: false,
},
},
});
}
export function MSALInterceptorConfigFactory(config: ConfigService): MsalInterceptorConfiguration {
const protectedResourceMap = new Map<string, Array<string>>(config.getSettings("interceptor").protectedResourceMap);
return {
interactionType: config.getSettings("interceptor").interactionType,
protectedResourceMap,
};
}
export function MSALGuardConfigFactory(config: ConfigService): MsalGuardConfiguration {
return {
interactionType: config.getSettings("guard").interactionType,
authRequest: config.getSettings("guard").authRequest,
loginFailedRoute: config.getSettings("guard").loginFailedRoute,
};
}
@NgModule({
providers: [],
imports: [MsalModule],
})
export class MsalConfigDynamicModule {
static forRoot(configFile: string) {
return {
ngModule: MsalConfigDynamicModule,
providers: [
ConfigService,
{ provide: AUTH_CONFIG_URL_TOKEN, useValue: configFile },
{ provide: APP_INITIALIZER, useFactory: initializerFactory, deps: [ConfigService, AUTH_CONFIG_URL_TOKEN], multi: true },
{
provide: MSAL_INSTANCE,
useFactory: MSALInstanceFactory,
deps: [ConfigService],
},
{
provide: MSAL_GUARD_CONFIG,
useFactory: MSALGuardConfigFactory,
deps: [ConfigService],
},
{
provide: MSAL_INTERCEPTOR_CONFIG,
useFactory: MSALInterceptorConfigFactory,
deps: [ConfigService],
},
MsalService,
MsalGuard,
MsalBroadcastService,
{
provide: HTTP_INTERCEPTORS,
useClass: MsalInterceptor,
multi: true,
},
],
};
}
}
src/app/app.module.ts
import { BrowserModule } from "@angular/platform-browser";
import { BrowserAnimationsModule } from "@angular/platform-browser/animations";
import { NgModule } from "@angular/core";
import { MatButtonModule } from "@angular/material/button";
import { MatToolbarModule } from "@angular/material/toolbar";
import { MatListModule } from "@angular/material/list";
import { AppRoutingModule } from "./app-routing.module";
import { AppComponent } from "./app.component";
import { HomeComponent } from "./home/home.component";
import { ProfileComponent } from "./profile/profile.component";
import { HttpClientModule } from "@angular/common/http";
import { MsalRedirectComponent } from "@azure/msal-angular";
import { DetailComponent } from "./detail/detail.component";
import { MsalConfigDynamicModule } from "./msal-config-dynamic.module";
@NgModule({
declarations: [AppComponent, HomeComponent, ProfileComponent, DetailComponent],
imports: [BrowserModule, BrowserAnimationsModule, AppRoutingModule, MatButtonModule, MatToolbarModule, MatListModule, HttpClientModule, MsalConfigDynamicModule.forRoot("assets/configuration.json")],
providers: [],
bootstrap: [AppComponent, MsalRedirectComponent],
})
export class AppModule {}
src/assets/configuration.json
{
"msal": {
"auth": {
"clientId": "clientid",
"authority": "https://login.microsoftonline.com/common/",
"redirectUri": "http://localhost:4200/",
"postLogoutRedirectUri": "http://localhost:4200/",
"navigateToLoginRequestUrl": true
},
"cache": {
"cacheLocation": "localStorage",
"storeAuthStateInCookie": true
}
},
"guard": {
"interactionType": "redirect",
"authRequest": {
"scopes": ["user.read"]
},
"loginFailedRoute": "/login-failed"
},
"interceptor": {
"interactionType": "redirect",
"protectedResourceMap": [["https://graph.microsoft.com/v1.0/me", ["user.read"]]]
}
}
MsalGuard - 动态身份验证请求
MsalGuard 还允许你在运行时动态更改 authRequest。 这允许你为某个路由选择不同的授权机构,或者根据 RouterStateSnapshot 动态添加作用域。
export function MSALGuardConfigFactory(): MsalGuardConfiguration {
return {
interactionType: InteractionType.Redirect,
authRequest: (authService, state) => {
return {
scopes: state.root.url.some((x) => x.path === "calendar") ? ["user.read", " Calendars.Read"] : ["user.read"],
};
},
loginFailedRoute: "./login-failed",
};
}
具有独立组件的 Angular 应用的配置
使用独立组件的 Angular 应用程序可以在 app.config.ts 文件中使用工厂提供程序,如上所示,然后导入到 main.ts 中以进行引导。
有关使用情况,请参阅 Angular 独立示例 。
// app.config.ts
import { ApplicationConfig, importProvidersFrom } from "@angular/core";
import { provideRouter } from "@angular/router";
import { routes } from "./app.routes";
import { BrowserModule } from "@angular/platform-browser";
import { provideHttpClient, withInterceptorsFromDi, HTTP_INTERCEPTORS, withFetch, withInterceptors } from "@angular/common/http";
import { provideNoopAnimations } from "@angular/platform-browser/animations";
import { IPublicClientApplication, PublicClientApplication, InteractionType, BrowserCacheLocation, LogLevel } from "@azure/msal-browser";
import { MsalInterceptor, MSAL_INSTANCE, MsalInterceptorConfiguration, MsalGuardConfiguration, MSAL_GUARD_CONFIG, MSAL_INTERCEPTOR_CONFIG, MsalService, MsalGuard, MsalBroadcastService } from "@azure/msal-angular";
export function loggerCallback(logLevel: LogLevel, message: string) {
console.log(message);
}
export function MSALInstanceFactory(): IPublicClientApplication {
return new PublicClientApplication({
auth: {
clientId: "clientid",
authority: "https://login.microsoftonline.com/common/",
redirectUri: "/",
postLogoutRedirectUri: "/",
},
cache: {
cacheLocation: BrowserCacheLocation.LocalStorage,
},
system: {
allowPlatformBroker: false, // Disables WAM Broker
loggerOptions: {
loggerCallback,
logLevel: LogLevel.Info,
piiLoggingEnabled: false,
},
},
});
}
export function MSALInterceptorConfigFactory(): MsalInterceptorConfiguration {
const protectedResourceMap = new Map<string, Array<string>>();
protectedResourceMap.set("https://graph.microsoft.com/v1.0/me", ["user.read"]);
return {
interactionType: InteractionType.Redirect,
protectedResourceMap,
};
}
export function MSALGuardConfigFactory(): MsalGuardConfiguration {
return {
interactionType: InteractionType.Redirect,
authRequest: {
scopes: ["user.read"],
},
loginFailedRoute: "/login-failed",
};
}
export const appConfig: ApplicationConfig = {
providers: [
provideRouter(routes),
importProvidersFrom(BrowserModule),
provideNoopAnimations(),
provideHttpClient(withInterceptorsFromDi(), withFetch()),
{
provide: HTTP_INTERCEPTORS,
useClass: MsalInterceptor,
multi: true,
},
{
provide: MSAL_INSTANCE,
useFactory: MSALInstanceFactory,
},
{
provide: MSAL_GUARD_CONFIG,
useFactory: MSALGuardConfigFactory,
},
{
provide: MSAL_INTERCEPTOR_CONFIG,
useFactory: MSALInterceptorConfigFactory,
},
MsalService,
MsalGuard,
MsalBroadcastService,
],
};
// main.ts
import { bootstrapApplication } from "@angular/platform-browser";
import { appConfig } from "./app/app.config";
import { AppComponent } from "./app/app.component";
bootstrapApplication(AppComponent, appConfig).catch((err) => console.error(err));