damienbod/bff-aspnetcore-angular

GitHub: damienbod/bff-aspnetcore-angular

展示如何以 BFF 模式将 Angular 与 ASP.NET Core 整合部署,通过服务端托管令牌实现安全的单页应用架构。

Stars: 140 | Forks: 25

# 使用 ASP.NET Core 和 Angular CLI 的 BFF 安全架构 [![.NET 和 npm 构建](https://static.pigsec.cn/wp-content/uploads/repos/2026/03/0384c4ac86075354.svg)](https://github.com/damienbod/bff-aspnetcore-angular/actions/workflows/dotnet.yml) [![构建并部署到 Azure Web App](https://static.pigsec.cn/wp-content/uploads/repos/2026/03/ece9ec446f075355.svg)](https://github.com/damienbod/bff-aspnetcore-angular/actions/workflows/azure-webapps-dotnet-core.yml) [![许可证](https://img.shields.io/badge/license-Apache%20License%202.0-blue.svg)](https://github.com/damienbod/bff-aspnetcore-angular/blob/main/LICENSE) ## 设置服务器 该 ASP.NET Core 项目已配置为可在开发和生产环境中运行。在生产环境中,它使用部署到 wwwroot 的 Angular 生产构建版本。在开发环境中,它使用 MS YARP 反向代理来转发请求。 ![BFF 生产环境](https://github.com/damienbod/bff-aspnetcore-angular/blob/main/images/bff-arch-production_01.drawio.png) 配置 YARP 反向代理以匹配 Angular CLI URL。这仅在开发环境中是必需的。我在开发中始终使用 HTTPS,并且端口需要与 Angular CLI 开发环境相匹配。 ``` { "UiDevServerUrl": "https://localhost:4201", "ReverseProxy": { "Routes": { "assets": { "ClusterId": "cluster1", "Match": { "Path": "assets/{**catch-all}" } }, "angularfsdev": { "ClusterId": "cluster1", "Match": { "Path": "@fs/{**catch-all}" } }, "angularngdev": { "ClusterId": "cluster1", "Match": { "Path": "@ng/{**catch-all}" } }, "vitedev": { "ClusterId": "cluster1", "Match": { "Path": "@vite/{**catch-all}" } }, "wssvite": { "ClusterId": "cluster1", "Match": { "Path": "/", "QueryParameters": [ { "Name": "token", "Mode": "Exists" } ] } }, "routealljs": { "ClusterId": "cluster1", "Match": { "Path": "{nomatterwhat}.js" } }, "routeallcss": { "ClusterId": "cluster1", "Match": { "Path": "{nomatterwhat}.css" } }, "webpacklazyloadingsources": { "ClusterId": "cluster1", "Match": { "Path": "/src_{nomatterwhat}_ts.js" } }, "webpacknodesrcmap": { "ClusterId": "cluster1", "Match": { "Path": "/{nomatterwhat}.js.map" } }, "wellknown": { "ClusterId": "cluster1", "Match": { "Path": ".well-known/{**catch-all}" } } }, "Clusters": { "cluster1": { "HttpClient": { "SslProtocols": ["Tls12"] }, "Destinations": { "cluster1/destination1": { "Address": "https://localhost:4201/" } } } } } } ``` ## 设置 Angular CLI 将证书添加到 CLI 项目中,例如放在 **/certs** 文件夹中。 更新 Angular CLI `angular.json` 文件: ``` ... "serve": { "builder": "@angular/build:dev-server", "options": { "sslKey": "certs/dev_localhost.key", "sslCert": "certs/dev_localhost.pem", "port": 4201 } } ... ``` 更新(Angular CLI 构建的)`outputPath`,以便将生产路径部署到 .NET 项目的 `wwwroot` 中 ``` "architect": { "build": { "builder": "@angular/build:application", "options": { "outputPath": { "base": "../server/wwwroot", "browser": "" }, "browser": "src/main.ts", "polyfills": ["zone.js"], "tsConfig": "tsconfig.app.json", "assets": [ { "glob": "**/*", "input": "public" } ], "styles": ["src/styles.css"] }, ``` ## 先决条件 Node >= v24.10.0 ``` npm install @angular/cli -g latest npm install --force npm run build ``` ## 设置开发环境 开发环境设置为使用各技术栈的默认工具。Angular CLI 按照推荐方式使用。我使用 Visual Studio Code。使用 YARP 反向代理将 Angular 开发环境集成到后端应用程序中。 ![BFF 开发环境](https://github.com/damienbod/bff-aspnetcore-angular/blob/main/images/bff-arch-development_01.drawio.png) ``` ng serve --ssl ``` ## Azure 应用注册设置 应用程序被部署为一个整体。这是一个 OpenID Connect 机密客户端,使用用户机密或证书进行客户端断言。 在设置时使用 Web 客户端类型。 ![BFF Azure 注册](https://github.com/damienbod/bff-aspnetcore-angular/blob/main/images/azure-app-registration_01.png) OpenID Connect 客户端使用 **Microsoft.Identity.Web** 进行设置。这实现了 Microsoft Entra ID 客户端。我使用 OBO 流和 Microsoft Graph 客户端创建了下游 API。这可以被任何 OpenID Connect 客户端替换,并且不需要在解决方案的前端部分进行任何更改。 ``` var scopes = configuration.GetValue("DownstreamApi:Scopes"); string[] initialScopes = scopes!.Split(' '); services.AddMicrosoftIdentityWebAppAuthentication(configuration, "MicrosoftEntraID") .EnableTokenAcquisitionToCallDownstreamApi(initialScopes) .AddMicrosoftGraph("https://graph.microsoft.com/v1.0", initialScopes) .AddInMemoryTokenCaches(); services.AddControllersWithViews(options => options.Filters.Add(new AutoValidateAntiforgeryTokenAttribute())); services.AddRazorPages().AddMvcOptions(options => { //var policy = new AuthorizationPolicyBuilder() // .RequireAuthenticatedUser() // .Build(); //options.Filters.Add(new AuthorizeFilter(policy)); }).AddMicrosoftIdentityUI(); ``` 将 Azure 应用注册设置添加到 **appsettings.Development.json**,并将 **ClientSecret** 添加到用户机密中。 ``` "MicrosoftEntraID": { "Instance": "https://login.microsoftonline.com/", "Domain": "[Enter the domain of your tenant, e.g. contoso.onmicrosoft.com]", "TenantId": "[Enter 'common', or 'organizations' or the Tenant Id (Obtained from the Azure portal. Select 'Endpoints' from the 'App registrations' blade and use the GUID in any of the URLs), e.g. da41245a5-11b3-996c-00a8-4d99re19f292]", "ClientId": "[Enter the Client Id (Application ID obtained from the Azure portal), e.g. ba74781c2-53c2-442a-97c2-3d60re42f403]", "ClientSecret": "[Copy the client secret added to the app from the Azure portal]", "ClientCertificates": [ ], // the following is required to handle Continuous Access Evaluation challenges "ClientCapabilities": [ "cp1" ], "CallbackPath": "/signin-oidc" }, ``` App Service(linux 计划)配置 ``` MicrosoftEntraID__Instance --your-value-- MicrosoftEntraID__Domain --your-value-- MicrosoftEntraID__TenantId --your-value-- MicrosoftEntraID__ClientId --your-value-- MicrosoftEntraID__CallbackPath /signin-oidc MicrosoftEntraID__SignedOutCallbackPath /signout-callback-oidc ``` 需要设置客户端机密或客户端证书,请参阅 Microsoft Entra ID 文档。 ## 调试 从 **ui** 文件夹启动 Angular 项目 ``` ng serve --ssl ``` 从 **server** 文件夹启动 ASP.NET Core 项目 ``` dotnet run ``` 或者直接打开 Visual Studio 并运行解决方案。 ## github actions 构建 Github actions 用于 DevOps。构建管道使用 npm 构建 .NET 项目和 Angular CLI 项目。这两个项目在同一步骤中构建,因为 UI 项目被构建到服务器项目的 wwwroot 中。 ``` name: .NET and npm build on: push: branches: ["main"] pull_request: branches: ["main"] jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Setup .NET uses: actions/setup-dotnet@v5 with: dotnet-version: 10.0.x - name: Restore dependencies run: dotnet restore - name: npm setup working-directory: ui run: npm install --force - name: ui-angular-cli-build working-directory: ui run: npm run build - name: Build run: dotnet build --no-restore - name: Test run: dotnet test --no-build --verbosity normal ``` ## github actions Azure 部署 部署管道构建这两个项目,并使用 Azure App Service 将其部署到 Azure。请参阅 **azure-webapps-dotnet-core.yml** 部署测试服务器:https://bff-angular-aspnetcore.azurewebsites.net ## 致谢和使用的库 - NetEscapades.AspNetCore.SecurityHeaders - Yarp.ReverseProxy - Microsoft.Identity.Web - ASP.NET Core - Angular, Angular CLI ## Angular CLI 更新 ``` npm install -g @angular/cli latest ng update ng update @angular/cli @angular/core ``` ## 链接 - [SonarQube Cloud - 分析 GitHub 项目](https://docs.sonarsource.com/sonarcloud/getting-started/github/) - [rufer7 - github-sonarcloud-integration](https://github.com/rufer7/github-sonarcloud-integration) - [[HOWTO] 在 Azure DevOps YAML 管道中集成 SonarCloud 分析](https://blog.rufer.be/2023/10/06/howto-integrate-sonarcloud-analysis-in-an-azure-devops-yaml-pipeline/) - [Sonar 社区 - .Net 代码覆盖率报告在 Linux 代理上不起作用](https://community.sonarsource.com/t/code-coverage-report-for-net-not-working-on-linux-agent/62087) - [SonarScanner for .NET - 分析 C# 和 VB 以外的语言](https://docs.sonarsource.com/sonarcloud/advanced-setup/ci-based-analysis/sonarscanner-for-net/#analyzing-languages-other-than-c-and-vb) - [Andrei Epure - 如何使用 Sonar Scanner for .NET 分析 JS/TS、HTML 和 CSS 文件](https://andreiepure.ro/2023/08/20/analyze-web-files-with-s4net.html) - [damienbod - bff-aspnetcore-angular](https://github.com/damienbod/bff-aspnetcore-angular) - [[网络研讨会] Web 应用程序中的端到端安全](https://community.sonarsource.com/t/webinar-end-to-end-security-in-a-web-application/115405)
标签:Angular, Angular CLI, ASP.NET Core, Azure Web App, Backend for Frontend, BFF 架构, Grype, HTTPS, Microsoft YARP, SPA 安全, TypeScript, Web 安全, Web 开发, YARP, 前后端分离, 单页应用, 反向代理, 安全插件, 开发环境配置, 架构模式, 生产环境部署, 网络安全, 隐私保护