MSAL、Angular 和 ASP.NET Core 出现 CORS 错误

D2O*_*D2O 5 azure-active-directory asp.net-core azure-ad-msal angular msal.js

我正在尝试构建一个 ASP.NET Core webapi + Angular 网站,用户可以使用 Microsoft 个人或工作或学校电子邮件登录。我按照此处描述的说明进行操作: https://github.com/AzureAD/microsoft-authentication-library-for-js/blob/dev/lib/msal-angular/README.md

但遇到这个问题,

  1. Angular网站加载,主页上受保护的组件触发登录过程

  2. 出现微软网站

  3. 我登录

  4. 浏览器加载以下网址:https://localhost:44321/#id_token=...&state=...

  5. 网站再次重新加载(登录后第二次)

  6. 我看到以下错误

    从源访问“https://login.microsoftonline.com/common/oauth2/v2.0/authorize?client_id=...”处的 XMLHttpRequest(从“https://localhost:44321/Environment/GetUserInfo”重定向) “https://localhost:44321”已被 CORS 策略阻止:对预检请求的响应未通过访问控制检查:请求的资源上不存在“Access-Control-Allow-Origin”标头。

使用 Visual Studio 2019 16.4.2 和 chrome 79.0.3945.88 进行本地调试时

任何想法?
谢谢

我已经使用创建了我的项目

dotnet new angular -o myapp
Run Code Online (Sandbox Code Playgroud)


并使用以下重定向 URI在 Azure.Portal Authentication 中创建应用程序注册

  • https://localhost:44321/signin-microsoft
  • https://login.microsoftonline.com/
  • http://本地主机:30662/
  • https://localhost:44321/signin-oidc
  • https://本地主机:44321/

检查了所有“公共客户端的建议重定向 URI”
注销 URL:https://localhost:44321/signout-callback-oidc
隐式授予:访问令牌和 ID 令牌
Live SDK 支持:是
默认客户端类型:否

证书和秘密
我创建了一个客户端秘密,因为我尝试使用 Microsoft 提供程序(请参阅下面的注释代码),然后尝试使用 AzureAd

API 权限
Microsoft.Graph User.Read

公开 API
范围 = [应用程序 ID URI]/access_as_user,仅限管理员
客户端应用程序 = [CLIENT_ID_FROM_AZURE_PORTAL],范围同上

服务器端
appsettings.json

 "AzureAd": {
    "Instance": "https://login.microsoftonline.com/",
    "Domain": "[Application ID URI]",
    "ClientId": "[CLIENT_ID_FROM_AZURE_PORTAL]",
    "TenantId": "common",
    "CallbackPath": "/signin-oidc"
  },
Run Code Online (Sandbox Code Playgroud)

启动.cs

public void ConfigureServices(IServiceCollection services)
{
  services.AddCors(options =>
  {
    options.AddPolicy("AllowAllOrigins",
        builder =>
        {
          builder
            .AllowAnyMethod()
            .AllowAnyHeader()
            .AllowAnyOrigin();
        });
  });

  services.Configure<CookiePolicyOptions>(options =>
  {
    // This lambda determines whether user consent for non-essential cookies is needed for a given request.
    options.CheckConsentNeeded = context => true;
    options.MinimumSameSitePolicy = SameSiteMode.None;
  });

  // In production, the Angular files will be served from this directory
  services.AddSpaStaticFiles(configuration =>
  {
    configuration.RootPath = "ClientApp/dist";
  });

  //services
  //  .AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
  //  .AddCookie(CookieAuthenticationDefaults.AuthenticationScheme)
  //  .AddMicrosoftAccount(microsoftOptions =>
  //{
  //  microsoftOptions.ClientId = "[CLIENT_ID_FROM_AZURE_PORTAL]";
  //  microsoftOptions.ClientSecret = "[CLIENT_SECRET_FROM_AZURE_PORTAL]";
  //});

  services.AddAuthentication(AzureADDefaults.AuthenticationScheme)
    .AddAzureAD(options => Configuration.Bind("AzureAd", options));

  services.Configure<OpenIdConnectOptions>(AzureADDefaults.OpenIdScheme, options =>
  {
    options.Authority = options.Authority + "/v2.0/";
    options.TokenValidationParameters.ValidateIssuer = false;
  });

  services.AddControllers(options =>
  {
    var policy = new AuthorizationPolicyBuilder()
                    .RequireAuthenticatedUser()
                    .Build();
    options.Filters.Add(new AuthorizeFilter(policy));
  })
  .SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
}

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
  app.UseCors("AllowAllOrigins");

  if (env.IsDevelopment())
  {
    app.UseDeveloperExceptionPage();
  }
  else
  {
    app.UseExceptionHandler("/Error");
    app.UseHsts();
  }

  app.UseHttpsRedirection();
  app.UseCookiePolicy();
  app.UseStaticFiles();
  if (!env.IsDevelopment())
  {
    app.UseSpaStaticFiles();
  }

  app.UseRouting();

  app.UseAuthentication();
  app.UseAuthorization();

  app.UseEndpoints(endpoints =>
  {
    endpoints.MapControllers();
  });

  app.UseSpa(spa =>
  {
    spa.Options.SourcePath = "ClientApp";

    if (env.IsDevelopment())
    {
      spa.UseAngularCliServer(npmScript: "start");
    }
  });
}
Run Code Online (Sandbox Code Playgroud)

客户端
app.module.ts

...
import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http';
import { MsalModule, MsalGuard, MsalInterceptor } from '@azure/msal-angular';
...
export const protectedResourceMap: [string, string[]][] = [
  ['https://localhost:44321/Environment/GetUserInfo', ['[Application ID URI]/access_as_user']],
  ['https://localhost:44321/api/Environment/GetUserInfo', ['[Application ID URI]/access_as_user']],
  ['https://graph.microsoft.com/v1.0/me', ['user.read']],
  ['https://login.microsoftonline.com/common', ['user.read']]
];

@NgModule({
  declarations: [
    AppComponent,
    NavMenuComponent,
    HomeComponent,
    CounterComponent,
    EntitySignoffComponent
  ],
  imports: [
    BrowserModule.withServerTransition({ appId: 'ng-cli-universal' }),
    HttpClientModule,
    FormsModule,
    AgGridModule.withComponents([]),
    MsalModule.forRoot({
      clientID: [CLIENT_ID_FROM_AZURE_PORTAL],
      authority: "https://login.microsoftonline.com/common",
      redirectUri: "https://localhost:44321/",
      validateAuthority: true,
      cacheLocation: "localStorage",
      storeAuthStateInCookie: false, // dynamically set to true when IE11
      postLogoutRedirectUri: "https://localhost:44321/",
      navigateToLoginRequestUrl: true,
      popUp: false,
      unprotectedResources: [ "https://login.microsoftonline.com/common" ],
      protectedResourceMap: protectedResourceMap
    }
    ),
    RouterModule.forRoot([
      { path: '', component: HomeComponent, pathMatch: 'full', canActivate: [MsalGuard] }
      { path: 'counter', component: CounterComponent, canActivate: [MsalGuard] },
    ])
  ],
  providers: [NavMenuComponent, { provide: HTTP_INTERCEPTORS, useClass: MsalInterceptor, multi: true }],
Run Code Online (Sandbox Code Playgroud)

包.json

{
  "name": "myapp",
  "version": "0.0.0",
  "scripts": {
    "ng": "ng",
    "start": "ng serve",
    "build": "ng build",
    "build:ssr": "ng run myapp:server:dev",
    "test": "ng test",
    "lint": "ng lint",
    "e2e": "ng e2e"
  },
  "private": true,
  "dependencies": {
    "@angular/animations": "8.2.14",
    "@angular/common": "^8.2.14",
    "@angular/compiler": "8.2.14",
    "@angular/core": "^8.2.14",
    "@angular/forms": "8.2.14",
    "@angular/platform-browser": "8.2.14",
    "@angular/platform-browser-dynamic": "8.2.14",
    "@angular/platform-server": "8.2.14",
    "@angular/router": "8.2.14",
    "@azure/msal-angular": "^0.1.4",
    "@nguniversal/module-map-ngfactory-loader": "8.2.6",
    "aspnet-prerendering": "^3.0.1",
    "bootstrap": "^4.4.1",
    "core-js": "^3.6.1",
    "jquery": "3.4.1",
    "oidc-client": "^1.10.1",
    "popper.js": "^1.16.0",
    "rxjs": "^6.5.4",
    "rxjs-compat": "^6.5.4",
    "zone.js": "0.10.2"
  },
  "devDependencies": {
    "@angular-devkit/build-angular": "^0.803.21",
    "@angular/cli": "8.3.21",
    "@angular/compiler-cli": "8.2.14",
    "@angular/language-service": "8.2.14",
    "@types/jasmine": "^3.5.0",
    "@types/jasminewd2": "~2.0.8",
    "@types/node": "~13.1.2",
    "codelyzer": "^5.2.1",
    "jasmine-core": "~3.5.0",
    "jasmine-spec-reporter": "~4.2.1",
    "karma": "^4.4.1",
    "karma-chrome-launcher": "~3.1.0",
    "karma-coverage-istanbul-reporter": "^2.1.1",
    "karma-jasmine": "~2.0.1",
    "karma-jasmine-html-reporter": "^1.5.1",
    "typescript": "3.5.3"
  },
  "optionalDependencies": {
    "node-sass": "^4.13.0",
    "protractor": "~5.4.2",
    "ts-node": "~8.5.4",
    "tslint": "~5.20.1"
  }
}
Run Code Online (Sandbox Code Playgroud)

Chrome 登录后出现错误

Sta*_*ong 3

据我了解,这两个官方样本完全可以满足您的要求。

我已经修改了官方的demo并集成给你了。请按照以下步骤使其工作:

  1. 对于API端。您可以在这里下载我的代码

1) 访问 Azure 门户并为其注册 Azure AD 应用程序: 在此输入图像描述

2)记下其应用程序 ID 并为其创建客户端密钥。将它们输入appsettings.json在此输入图像描述

3)回到Azure门户,公开一个API,以便客户端可以请求它的权限: 在此输入图像描述

4)API端工作完成,可以直接运行该项目。

  1. 对于有角度的一侧,您可以在此处获取我的代码

1) 为此客户端注册 Azure AD 应用程序: 在此输入图像描述

2)为此应用程序添加权限,以便它可以访问您的后端: 在此输入图像描述 在此输入图像描述 单击授予权限按钮完成该过程: 在此输入图像描述 这两个演示均基于 Azure AD V2.0 端点,允许个人帐户和学校工作帐户登录并执行一些操作。

3)在Auth Blade上进行一些配置,以便它可以获得作为公共客户端所需的令牌: 在此输入图像描述

4)所有步骤都是针对角度侧完成的,npm start要运行它,访问它:http://localhost:44302/ 我已经在本地进行了测试,它非常适合我: 在此输入图像描述

如果您不知道如何组合这两个应用程序,请告诉我。