共用方式為


建立安全的 .NET 微服務和 Web 應用程式

小提示

此內容是適用於容器化 .NET 應用程式的電子書.NET 微服務架構摘錄,可在 .NET Docs 或免費下載的 PDF 中取得,可脫機讀取。

.NET 微服務架構的容器化 .NET 應用程式電子書封面縮圖。

微服務和 Web 應用程式的安全性包含許多方面,這個主題可以輕易成為需要多本書探討的內容,如同這一本書。 因此,在本節中,我們將著重於驗證、授權和應用程式秘密。

在 .NET 微服務和 Web 應用程式中實作驗證

服務所發行的資源和 API 通常必須受限於特定受信任的使用者或用戶端。 進行這類 API 層級信任決策的第一個步驟是驗證。 認證是一個可靠核實使用者身分的過程。

在微服務案例中,驗證通常會集中處理。 如果您使用 API 閘道,閘道是驗證的好位置,如圖 9-1 所示。 如果您使用此方法,請確定無法直接連線到個別微服務(不含 API 閘道),除非有額外的安全性,以驗證訊息是否來自閘道。

顯示用戶端應用程式如何與後端互動的圖表。

圖 9-1。 使用 API 閘道進行集中式驗證

當 API 閘道集中驗證時,它會在將要求轉送至微服務時新增使用者資訊。 如果可以直接存取服務,則可以使用 Azure Active Directory 之類的驗證服務或作為安全性令牌服務 (STS) 的專用驗證微服務來驗證使用者。 信任決策會在服務與安全性權杖或 Cookie 之間共用。 (如有需要,您可以透過實作 Cookie 共用,在 ASP.NET Core 應用程式之間共用這些令牌。)圖 9-2 說明此模式。

顯示透過後端微服務進行驗證的圖表。

圖 9-2。 透過身分驗證微服務進行身份確認;信任透過授權令牌來共享

直接存取微服務時,信任,包括驗證和授權,是由專用微服務所簽發的安全性令牌所處理,在微服務之間共用。

使用 ASP.NET 核心身分識別進行驗證

ASP.NET Core 中用來識別應用程式使用者的主要機制是 ASP.NET 核心身分識別 成員資格系統。 ASP.NET Core Identity 會將使用者資訊 (包括登入資訊、角色和宣告) 儲存在開發人員所設定的資料存放區中。 一般而言,ASP.NET Core Identity 數據存放區是套件中 Microsoft.AspNetCore.Identity.EntityFrameworkCore 提供的 Entity Framework 存放區。 不過,自定義存放區或其他第三方套件可用來將身分識別資訊儲存在 Azure 表格記憶體、CosmosDB 或其他位置。

小提示

ASP.NET Core 2.1 和更新版本會提供 ASP.NET Core Identity 作為 Razor 類別庫,因此您不會看到專案中的大部分必要程式代碼,就像舊版的情況一樣。 如需如何自定義身分識別程序代碼以符合您的需求的詳細資訊,請參閱 ASP.NET Core 專案中的 Scaffold Identity

下列程式代碼取自已選取個別用戶帳戶驗證的 ASP.NET Core Web 應用程式 MVC 專案範本。 它會示範如何在 Program.cs 檔案中使用 Entity Framework Core 設定 ASP.NET Core 身分識別。

//...
builder.Services.AddDbContext<ApplicationDbContext>(options =>
    options.UseSqlServer(
        builder.Configuration.GetConnectionString("DefaultConnection")));

builder.Services.AddDefaultIdentity<IdentityUser>(options =>
    options.SignIn.RequireConfirmedAccount = true)
        .AddEntityFrameworkStores<ApplicationDbContext>();

builder.Services.AddRazorPages();
//...

設定 ASP.NET Core Identity 之後,您可以藉由在服務的 app.UseAuthentication() 檔案中新增 endpoints.MapRazorPages(),如以下程式碼所示來啟用它:

//...
app.UseRouting();

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

app.UseEndpoints(endpoints =>
{
    endpoints.MapRazorPages();
});
//...

這很重要

上述程式代碼中的行 必須依顯示的順序顯示 ,才能讓身分識別正常運作。

使用 ASP.NET Core Identity 可啟用數個案例:

  • 使用 UserManager 類型建立新的使用者資訊(userManager.CreateAsync)。

  • 使用 SignInManager 類型驗證使用者。 您可以使用 signInManager.SignInAsync 直接登入,或 signInManager.PasswordSignInAsync 確認使用者的密碼正確無誤,然後登入。

  • 根據 Cookie 中儲存的資訊來識別使用者(由 ASP.NET Core Identity 中間件讀取),讓瀏覽器的後續要求包含登入使用者的身分識別和宣告。

ASP.NET Core Identity 也支援 雙因素驗證

針對使用本機用戶數據存放區的驗證案例,以及在使用 Cookie 的要求之間保存身分識別(如同 MVC Web 應用程式的典型情況),ASP.NET 核心身分識別是建議的解決方案。

向外部提供者進行驗證

ASP.NET Core 也支援使用 外部驗證提供者 讓使用者透過 OAuth 2.0 流程登入。 這表示使用者可以使用來自Microsoft、Google、Facebook 或 Twitter 等提供者的現有驗證程式進行登入,並將這些身分識別與應用程式中 ASP.NET 核心身分識別產生關聯。

若要使用外部驗證,除了包含先前所述的驗證中間件之外,您還必須使用app.UseAuthentication()方法在Program.cs中註冊外部驗證提供者,如下列範例所示:

//...
services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true)
    .AddEntityFrameworkStores<ApplicationDbContext>();

services.AddAuthentication()
    .AddMicrosoftAccount(microsoftOptions =>
    {
        microsoftOptions.ClientId = builder.Configuration["Authentication:Microsoft:ClientId"];
        microsoftOptions.ClientSecret = builder.Configuration["Authentication:Microsoft:ClientSecret"];
    })
    .AddGoogle(googleOptions => { ... })
    .AddTwitter(twitterOptions => { ... })
    .AddFacebook(facebookOptions => { ... });
//...

下表顯示熱門的外部驗證提供者及其相關聯的 NuGet 套件:

提供者 包裹
微軟 Microsoft.AspNetCore.Authentication.MicrosoftAccount
谷歌 Microsoft.AspNetCore.Authentication.Google
Facebook Microsoft.AspNetCore.Authentication.Facebook
推特 Microsoft.AspNetCore.Authentication.Twitter

在所有情況下,您都必須完成與廠商相依的應用程式註冊程式,且通常涉及:

  1. 取得用戶端應用程式識別碼。
  2. 取得用戶端應用程式密碼。
  3. 由授權中介軟體和已註冊的提供者處理的重新導向 URL 設定
  4. 您可以選擇設定登出 URL,以在單一登入(SSO)場景中正確處理登出。

如需為外部提供者設定應用程式的詳細資訊,請參閱 ASP.NET Core 檔中的外部提供者驗證

小提示

所有詳細數據都會由先前提及的授權中間件和服務處理。 因此,除了註冊先前所述的驗證提供者之外,您只需要在Visual Studio中建立 ASP.NET Core Web 應用程式專案時,選擇 [個別使用者帳戶 驗證] 選項。

[新增 ASP.NET Core Web 應用程式] 對話框的螢幕快照。

圖 9-3。 在 Visual Studio 2019 中建立 Web 應用程式專案時,選取 [個別使用者帳戶] 選項以使用外部驗證。

除了先前列出的外部驗證提供者之外,第三方套件還提供中間件,以便使用更多外部驗證提供者。 如需清單,請參閱 GitHub 上的 AspNet.Security.OAuth.Providers 存放庫。

您也可以建立自己的外部驗證中間件,以解決一些特殊需求。

使用持有人令牌進行驗證

使用 ASP.NET Core Identity 進行驗證(或身分識別加上外部驗證提供者)適用於將使用者資訊儲存在 Cookie 中的許多 Web 應用程式案例。 不過,在其他案例中,Cookie 並不是保存和傳輸數據的自然方法。

例如,在 ASP.NET Core Web API 中,公開可由單頁應用程式(SPA)、原生用戶端或甚至是其他 Web API 存取的 RESTful 端點,您通常想要改用持有人令牌驗證。 這些類型的應用程式不使用 Cookie,但可以輕鬆地擷取持憑令牌,並將其包含在後續請求的授權標頭中。 若要啟用令牌驗證,ASP.NET Core 支援使用 OAuth 2.0OpenID Connect 的數個選項。

使用 OpenID Connect 或 OAuth 2.0 識別提供者進行驗證

如果使用者資訊儲存在 Azure Active Directory 或其他支援 OpenID Connect 或 OAuth 2.0 的身分識別解決方案中,您可以使用 Microsoft.AspNetCore.Authentication.OpenIdConnect 套件來使用 OpenID Connect 工作流程進行驗證。 例如,若要向 eShopOnContainers 中的 Identity.Api 微服務進行驗證,ASP.NET Core Web 應用程式可以使用該套件的中間件,如下列簡化範例所示 ,Program.cs

// Program.cs

var identityUrl = builder.Configuration.GetValue<string>("IdentityUrl");
var callBackUrl = builder.Configuration.GetValue<string>("CallBackUrl");
var sessionCookieLifetime = builder.Configuration.GetValue("SessionCookieLifetimeMinutes", 60);

// Add Authentication services

services.AddAuthentication(options =>
{
    options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
    options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddCookie(setup => setup.ExpireTimeSpan = TimeSpan.FromMinutes(sessionCookieLifetime))
.AddOpenIdConnect(options =>
{
    options.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
    options.Authority = identityUrl.ToString();
    options.SignedOutRedirectUri = callBackUrl.ToString();
    options.ClientId = useLoadTest ? "mvctest" : "mvc";
    options.ClientSecret = "secret";
    options.ResponseType = useLoadTest ? "code id_token token" : "code id_token";
    options.SaveTokens = true;
    options.GetClaimsFromUserInfoEndpoint = true;
    options.RequireHttpsMetadata = false;
    options.Scope.Add("openid");
    options.Scope.Add("profile");
    options.Scope.Add("orders");
    options.Scope.Add("basket");
    options.Scope.Add("marketing");
    options.Scope.Add("locations");
    options.Scope.Add("webshoppingagg");
    options.Scope.Add("orders.signalrhub");
});

// Build the app
//…
app.UseAuthentication();
//…
app.UseEndpoints(endpoints =>
{
    //...
});

當您使用此工作流程時,不需要 ASP.NET Core Identity 中間件,因為身分識別服務會處理所有使用者資訊儲存和驗證。

從 ASP.NET Core 服務發出安全性令牌

如果您希望為本機 ASP.NET Core 身分識別使用者發出安全性令牌,而不是使用外部身分識別提供者,您可以利用一些良好的第三方程式庫。

IdentityServer4OpenIddict 是 OpenID Connect 提供者,可與 ASP.NET Core Identity 輕鬆整合,讓您從 ASP.NET Core 服務發出安全性令牌。 IdentityServer4 文件有使用函式庫的詳細說明。 不過,使用 IdentityServer4 發行令牌的基本步驟如下。

  1. 您可以在 Program.cs 中設定 IdentityServer4,方法是呼叫建立器。Services.AddIdentityServer。

  2. Program.cs 中呼叫 app.UseIdentityServer,將 IdentityServer4 添加到應用程式的 HTTP 要求處理管線。 這可讓程式庫處理對 OpenID Connect 和 OAuth2 端點的請求,例如 /connect/token。

  3. 您可以藉由設定下列資料來設定身分識別伺服器:

    • 要用於簽署的認證。

    • 使用者可能會要求存取的 身分識別和 API 資源

      • API 資源代表使用者可以使用存取令牌存取的受保護數據或功能。 API 資源的範例是需要授權的 Web API(或一組 API)。

      • 身分識別資源代表提供給客戶端識別使用者的資訊(宣告)。 宣告可能包含使用者名稱、電子郵件位址等等。

    • 將要連線以請求令牌的 用戶端

    • 使用者資訊的儲存機制,例如 ASP.NET 核心身分識別 或替代方案。

當您指定要使用 IdentityServer4 的用戶端和資源時,您可以將適當類型的集合傳遞 IEnumerable<T> 至採用記憶體內部用戶端或資源存放區的方法。 或者,針對更複雜的案例,您可以透過相依性插入提供用戶端或資源提供者類型。

IdentityServer4 使用記憶體內部資源和自定義 IClientStore 類型提供的用戶端範例組態,看起來可能如下列範例所示:

// Program.cs

builder.Services.AddSingleton<IClientStore, CustomClientStore>();
builder.Services.AddIdentityServer()
    .AddSigningCredential("CN=sts")
    .AddInMemoryApiResources(MyApiResourceProvider.GetAllResources())
    .AddAspNetIdentity<ApplicationUser>();
//...

使用安全性令牌

針對 OpenID Connect 端點進行驗證或發出您自己的安全性令牌可涵蓋某些情境。 但是,有需要限制存取權的服務,這些使用者必須持有由其他服務提供的有效安全性令牌,該怎麼辦呢?

在該案例中,處理 JWT 令牌的驗證中間件可在 Microsoft.AspNetCore.Authentication.JwtBearer 套件中使用。 JWT 代表「JSON Web 令牌」,且是用來通訊安全性宣告的常見安全性令牌格式(由 RFC 7519 定義)。 如何使用中間件來取用這類令牌的簡化範例,可能看起來像這個代碼段,取自 eShopOnContainers 的 Ordering.Api 微服務。

// Program.cs

var identityUrl = builder.Configuration.GetValue<string>("IdentityUrl");

// Add Authentication services

builder.Services.AddAuthentication(options =>
{
    options.DefaultAuthenticateScheme = AspNetCore.Authentication.JwtBearer.JwtBearerDefaults.AuthenticationScheme;
    options.DefaultChallengeScheme = AspNetCore.Authentication.JwtBearer.JwtBearerDefaults.AuthenticationScheme;

}).AddJwtBearer(options =>
{
    options.Authority = identityUrl;
    options.RequireHttpsMetadata = false;
    options.Audience = "orders";
});

// Build the app

app.UseAuthentication();
//…
app.UseEndpoints(endpoints =>
{
    //...
});

此使用方式中的參數如下:

  • Audience 表示傳入令牌的接收者,或令牌授與存取權的資源。 如果此參數中指定的值不符合令牌中的 參數,則會拒絕令牌。

  • Authority 是令牌發行驗證伺服器的位址。 JWT 持有人驗證中間件會使用此 URI 來取得可用來驗證令牌簽章的公鑰。 中間件也會確認 iss 令牌中的 參數符合此 URI。

另一個參數 RequireHttpsMetadata,適用於測試目的;您可以將此參數設定為 false,以便在沒有憑證的環境中進行測試。 在真實世界的部署中,應該一律只透過 HTTPS 傳遞 JWT 持有人令牌。

在使用此中間件時,JWT 令牌會自動從授權標頭中提取。 然後,它們會還原串行化、驗證(使用 和 Audience 參數中的Authority值),並儲存為用戶資訊,以供 MVC 動作或授權篩選稍後參考。

JWT 持有者驗證中間件也可以支援更進階的情境,例如,在授權伺服器無法使用時,使用本機憑證來驗證令牌。 在此情境中,您可以在TokenValidationParameters物件中指定JwtBearerOptions物件。

其他資源