3

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

但是遇到这个问题,

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

  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


并使用以下重定向 URI在 Azure.Portal 身份验证中创建了一个应用注册

  • https://localhost:44321/signin-microsoft
  • https://login.microsoftonline.com/
  • http://localhost:30662/
  • https://localhost:44321/signin-oidc
  • https://localhost: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"
  },

启动.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");
    }
  });
}

客户端
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 }],

包.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"
  }
}

登录后 Chrome 出现错误

4

2 回答 2

1

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

我已经修改了官方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刀片上做一些配置,以便它可以获得作为公共客户端所需的令牌: 在此处输入图像描述

4)所有步骤都针对 angular side 完成,npm start运行它,访问它:http://localhost:44302/ 我已经在我的本地测试过,它对我来说非常有效: 在此处输入图像描述

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

于 2020-01-10T05:24:13.570 回答
0

这个问题没有太多的赞成票,但我在使用 MSAL 时也遇到了 CORS 错误。对我来说,这是由于为 @azure/msal-angular 选择的身份验证方案与后端预期的身份验证方案不匹配造成的。

就我而言,两者都需要是 JWTBearer 或“Bearer {token}”。

您获得 CORS 的原因是不匹配会触发后端尝试与 microsoft 进行身份验证,从而导致重定向(和跨域请求)。通过调整前端和后端身份验证方案,问题就消失了。

于 2021-11-18T03:13:11.767 回答