使用带有 Angular 13 和 .NET 核心 Web api 的 Azure Active Directory 进行身份验证
Authenticating using Azure Active Directory with Angular 13 and .NET core web api
我有一个用于后端的 .NET CORE 6 Api 和一个用于前端的 Angular 13。目前我正在尝试使用 msal
通过 Angular 进行身份验证,然后调用受保护的 .net 核心 API。我想我错过了什么。 Api 是 weatherforcast
Api 模板。它通过 AAD 成功验证,这让我可以获取用户信息。但是,当我尝试通过调用 http.get
方法通过 api 请求数据时,它没有授权。受保护的API如何从前端获取授权?这两个应用程序都在 Azure 活动目录中注册。根据我的理解,HTTP_INTERCEPTORS
应该拦截访问令牌,并且应该授予 api 的授权。 ProfileComponent
有 MsalGuard
.
app.module.ts
import { HTTP_INTERCEPTORS, HttpClientModule } from '@angular/common/http';
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { Configuration } from 'msal';
import { AppComponent } from './app.component';
import { AppRoutingModule } from './app-routing.module';
import { BrowserCacheLocation, InteractionType, IPublicClientApplication, LogLevel, PublicClientApplication } from '@azure/msal-browser';
import { MsalGuard, MsalInterceptor, MsalBroadcastService, MsalInterceptorConfiguration, MsalModule, MsalService, MSAL_GUARD_CONFIG, MSAL_INSTANCE, MSAL_INTERCEPTOR_CONFIG, MsalGuardConfiguration, MsalRedirectComponent } from '@azure/msal-angular';
import { ProfileComponent } from './profile/profile.component';
import { environment } from '../environments/environment';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { MatFormFieldModule } from '@angular/material/form-field'
import { MatCheckboxModule } from '@angular/material/checkbox'
import { MatInputModule } from '@angular/material/input'
import { MatButtonModule } from '@angular/material/button'
import { MatDividerModule } from '@angular/material/divider'
const isIE = window.navigator.userAgent.indexOf("MSIE ") > -1 || window.navigator.userAgent.indexOf("Trident/") > -1;
export function loggerCallback(logLevel: LogLevel, message: string) {
console.log(message);
}
export function MSALInstanceFactory(): IPublicClientApplication {
return new PublicClientApplication({
auth: {
clientId: environment.clientId,
authority: environment.authority,
redirectUri: environment.redirectUrl,
postLogoutRedirectUri: environment.redirectUrl,
navigateToLoginRequestUrl: true
},
cache: {
cacheLocation: BrowserCacheLocation.LocalStorage,
storeAuthStateInCookie: isIE, // set to true for IE 11
},
system: {
loggerOptions: {
loggerCallback,
logLevel: LogLevel.Info,
piiLoggingEnabled: false
}
}
});
}
export function MSALInterceptorConfigFactory(): MsalInterceptorConfiguration {
const protectedResourceMap = new Map<string, Array<string>>();
protectedResourceMap.set('api://{uri id}', ['name.read']);
return {
interactionType: InteractionType.Redirect,
protectedResourceMap
};
}
export function MSALGuardConfigFactory(): MsalGuardConfiguration {
return { interactionType: InteractionType.Redirect };
}
@NgModule({
declarations: [
AppComponent,
ProfileComponent
],
imports: [
BrowserModule,
HttpClientModule,
AppRoutingModule,
MsalModule,
BrowserAnimationsModule,
MatCheckboxModule,
MatInputModule,
MatButtonModule,
MatFormFieldModule,
MatDividerModule
],
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
},
MsalService,
MsalGuard,
MsalBroadcastService
],
bootstrap: [AppComponent, MsalRedirectComponent]
})
export class AppModule { }
profile.component.ts
import { Component, OnInit } from '@angular/core';
import { MsalService } from '@azure/msal-angular';
import { ApiService } from '../services/api.service';
@Component({
selector: 'app-profile',
templateUrl: './profile.component.html',
styleUrls: ['./profile.component.css']
})
export class ProfileComponent implements OnInit {
forecasts: any;
name: any;
username: any;
constructor(private _apiService: ApiService, private _msalService: MsalService) { }
ngOnInit(): void {
const account = this._msalService.instance.getAllAccounts()[0];
this.name = account?.name;
this.username = account?.username;
this._apiService.getForcast().subscribe(result => {
this.forecasts = result;
}, error => console.error(error));
}
}
appsettings.json
"AzureAd": {
"Instance": "https://login.microsoftonline.com/",
"Domain": "example.com",
"TenantId": "{TenantId}",
"ClientId": "{ClientId}",
"Scopes": "name.write name.read",
"CallbackPath": "/signin-oidc",
"Audience": "api://{uri id}"
},
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*"
您可能想要检查受保护的资源地图。您当前拥有“api://{uri id}”。尝试将实际的 API url 放在这里只是为了测试一下。类似于...“http://localhost:1448/api/weatherforecasts”,或任何端点。拨打电话时,请查看授权 header 是否在请求中。
我有一个用于后端的 .NET CORE 6 Api 和一个用于前端的 Angular 13。目前我正在尝试使用 msal
通过 Angular 进行身份验证,然后调用受保护的 .net 核心 API。我想我错过了什么。 Api 是 weatherforcast
Api 模板。它通过 AAD 成功验证,这让我可以获取用户信息。但是,当我尝试通过调用 http.get
方法通过 api 请求数据时,它没有授权。受保护的API如何从前端获取授权?这两个应用程序都在 Azure 活动目录中注册。根据我的理解,HTTP_INTERCEPTORS
应该拦截访问令牌,并且应该授予 api 的授权。 ProfileComponent
有 MsalGuard
.
app.module.ts
import { HTTP_INTERCEPTORS, HttpClientModule } from '@angular/common/http';
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { Configuration } from 'msal';
import { AppComponent } from './app.component';
import { AppRoutingModule } from './app-routing.module';
import { BrowserCacheLocation, InteractionType, IPublicClientApplication, LogLevel, PublicClientApplication } from '@azure/msal-browser';
import { MsalGuard, MsalInterceptor, MsalBroadcastService, MsalInterceptorConfiguration, MsalModule, MsalService, MSAL_GUARD_CONFIG, MSAL_INSTANCE, MSAL_INTERCEPTOR_CONFIG, MsalGuardConfiguration, MsalRedirectComponent } from '@azure/msal-angular';
import { ProfileComponent } from './profile/profile.component';
import { environment } from '../environments/environment';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { MatFormFieldModule } from '@angular/material/form-field'
import { MatCheckboxModule } from '@angular/material/checkbox'
import { MatInputModule } from '@angular/material/input'
import { MatButtonModule } from '@angular/material/button'
import { MatDividerModule } from '@angular/material/divider'
const isIE = window.navigator.userAgent.indexOf("MSIE ") > -1 || window.navigator.userAgent.indexOf("Trident/") > -1;
export function loggerCallback(logLevel: LogLevel, message: string) {
console.log(message);
}
export function MSALInstanceFactory(): IPublicClientApplication {
return new PublicClientApplication({
auth: {
clientId: environment.clientId,
authority: environment.authority,
redirectUri: environment.redirectUrl,
postLogoutRedirectUri: environment.redirectUrl,
navigateToLoginRequestUrl: true
},
cache: {
cacheLocation: BrowserCacheLocation.LocalStorage,
storeAuthStateInCookie: isIE, // set to true for IE 11
},
system: {
loggerOptions: {
loggerCallback,
logLevel: LogLevel.Info,
piiLoggingEnabled: false
}
}
});
}
export function MSALInterceptorConfigFactory(): MsalInterceptorConfiguration {
const protectedResourceMap = new Map<string, Array<string>>();
protectedResourceMap.set('api://{uri id}', ['name.read']);
return {
interactionType: InteractionType.Redirect,
protectedResourceMap
};
}
export function MSALGuardConfigFactory(): MsalGuardConfiguration {
return { interactionType: InteractionType.Redirect };
}
@NgModule({
declarations: [
AppComponent,
ProfileComponent
],
imports: [
BrowserModule,
HttpClientModule,
AppRoutingModule,
MsalModule,
BrowserAnimationsModule,
MatCheckboxModule,
MatInputModule,
MatButtonModule,
MatFormFieldModule,
MatDividerModule
],
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
},
MsalService,
MsalGuard,
MsalBroadcastService
],
bootstrap: [AppComponent, MsalRedirectComponent]
})
export class AppModule { }
profile.component.ts
import { Component, OnInit } from '@angular/core';
import { MsalService } from '@azure/msal-angular';
import { ApiService } from '../services/api.service';
@Component({
selector: 'app-profile',
templateUrl: './profile.component.html',
styleUrls: ['./profile.component.css']
})
export class ProfileComponent implements OnInit {
forecasts: any;
name: any;
username: any;
constructor(private _apiService: ApiService, private _msalService: MsalService) { }
ngOnInit(): void {
const account = this._msalService.instance.getAllAccounts()[0];
this.name = account?.name;
this.username = account?.username;
this._apiService.getForcast().subscribe(result => {
this.forecasts = result;
}, error => console.error(error));
}
}
appsettings.json
"AzureAd": {
"Instance": "https://login.microsoftonline.com/",
"Domain": "example.com",
"TenantId": "{TenantId}",
"ClientId": "{ClientId}",
"Scopes": "name.write name.read",
"CallbackPath": "/signin-oidc",
"Audience": "api://{uri id}"
},
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*"
您可能想要检查受保护的资源地图。您当前拥有“api://{uri id}”。尝试将实际的 API url 放在这里只是为了测试一下。类似于...“http://localhost:1448/api/weatherforecasts”,或任何端点。拨打电话时,请查看授权 header 是否在请求中。