基於REACT和.NET CORE整合WINDOWS身份驗證

語言: CN / TW / HK

有很多方法可以向您的應用程式新增身份驗證。雖然OAuth是最常見的一種,但這並不是您唯一的選擇。今天,我將向您展示如何通過React和.NET Core簡單地完成Windows身份驗證。

探索我們的選擇

在深入探討之前,讓我們簡要討論一些可用的其他選項。瞭解您的選擇,可以使您根據自己的情況做出最佳(受過良好教育)的決定。這絕不是關於替代方案的詳盡討論,而只是其中一些較流行的替代方案。

Okta 是一家身份和訪問管理公司,提供基於雲的解決方案。他們有 Active Directory 外掛/提供程式 。他們的站點包含一個教程,如何 開始向 您的React應用程式 新增身份驗證 。在商業解決方案方面,該解決方案經常被推薦使用。

Auth0 是另一個具有良好關注度的商業解決方案。他們有專門針對將 Active Directory / LDAP與React結合 使用的教程。

IdentityServer 是開源替代方案。就像其他人一樣,他們提供了有關 如何實施Windows身份驗證的說明 。他們有一個有關 如何實現javascript客戶端 (例如React)的示例。這是有關 在Identity Server 4中使用SPA(反應/角度)UI的文章

當然,我們今天在這裡討論的選擇是推出您自己的解決方案。

為什麼不選擇交鑰匙解決方案?

儘管您的原因可能有所不同,但我想出了一些原因。

  1. 基礎結構/資源不足,無法設定服務(適用於Identity Server 4)。

  2. 資金/預算不足,無法支付SAAS提供商的費用。

  3. 您位於防火牆之內,並且React和.NET應用程式都位於同一網路上。

  4. 您喜歡挑戰和/或喜歡重新發明輪子(哈!)

好的,也許最後一個有點有趣。一般來說,使用交鑰匙解決方案 最好的選擇,但不是 您的 選擇。在我遇到的這個特殊用例中,這不是我想要的。然而。

入門

為了構建此應用程式,我們需要兩件事:

  1. .NET Core API專案–該專案將處理身份驗證,授權以及API呼叫

  2. React應用程式–該專案是我們的GUI

我為該專案學習和應用的東西是Google-fu精通和反覆試驗編碼的結合。希望彙總我的經驗將使您(我的讀者)比我更容易。

我首先建立一個資料夾來包含我的API和React專案,然後執行 dotnet new api -o ReactWindowsAuth 以搭建一個新的API專案。(這實際上是一個謊言,我使用VS來建立應用程式,但是我們假裝使用了CLI)。從那裡開始,我執行 npm create-react-app test-app 了一個名為的基本React應用程式 test-app 。就個人而言,我喜歡將Visual Studio用於.NET程式碼,將VSCode用於幾乎所有其他內容,因此我在VS中打開了新的API專案並生成了解決方案檔案。

從這裡開始,我們現在可以開始使用React和.NET Core實施Windows身份驗證了!

框架.NET Core API

我們需要做的第一件事就是確保我們的應用程式以Windows身份驗證執行。由於我使用VS生成了專案並提供了Docker支援,因此我不得不做一些您可能不需要做的事情。

啟用Windows身份驗證

我要做的第一件事是將除錯啟動器從Docker切換到IIS Express。

切換預設啟動

接下來,我需要開啟我的 launchSettings.json "windowsAuthentication": true iisSettings 下進行設定。

啟用Windows身份驗證

好吧,讓我們稍等一秒鐘。為了使Windows身份驗證起作用,您將需要在IIS或IIS Express中進行託管。您也可以使用 Kestrel HTTP.sys 託管來完成此操作,但出於本文的方便,讓我們集中討論IIS Express。如果您想使它在Docker和/或Linux上執行,您將要使用Kestrel。

我們的應用程式現在可以與Windows身份驗證一起使用了,但是如果我們現在啟動它,它仍然不會使用。我們還有很多工作要做。

配置Windows身份驗證

現在我們已經設定了API以通過IIS使用Windows身份驗證,我們需要使API本身意識到這一點。為此,我們需要對進行一些調整 Startup.cs MSDN上有文件 ,但我們也可以在這裡進行閱讀。

您需要做的第一件事是 services.AddAuthentication(IISDefaults.AuthenticationScheme); 在您的 ConfigureServices 方法中新增任何位置。這利用了 Microsoft.AspNetCore.Server.IISIntegration 名稱空間。

新增Windows身份驗證

接下來,您需要配置Windows身份驗證需要保護哪些控制器或動作。順便說一下,這就是為什麼我們 "anonymousAuthentication": true 獨自留在 launchSettings.json 。這裡的一個用例是,如果您使用的是Swagger,並且希望匿名訪問文件並且僅保護API本身。

在我的用例中,我假裝我希望所有API控制器都需要身份驗證。鑑於此,我建立了一個 WebControllerBase.cs ,並用 [Authorize] 屬性對其進行了裝飾。請注意,您可以改為用[Authorize]標記每個控制器。就是說,我的理由實際上是押韻的,在以後的第二部分中,我將進一步闡述這個想法。現在,只需滾動即可。

WebControllerBase,將[Authorize]標記為需要身份驗證

接下來,只需讓我們現有的控制器和新控制器繼承自 WebControllerBase 而不是即可 ControllerBase

現在,我們現有的控制器繼承自WebControllerBase

在網路上的其他示例中,您可能會看到人們說您需要新增 app.UseAuthentication() Configure(IApplicationBuilder app, IWebHostEnvironment env) 方法。如果僅針對IIS / IIS Express,則不會。也就是說,新增它不會傷害您。我不會判斷你是否願意。我的原始碼有它。

測試一下!

現在,我們可以對其進行測試了。在WeatherForecastController頂部打一個斷點:以除錯模式獲取並執行Web應用程式。達到斷點時,新增監視 HttpContext.User 並向下鑽取 .Identity.Name 。(可選)只需在方法頂部新增此行: var user = HttpContext.User?.Identity?.Name ?? "N/A"; 並檢視結果。您的應用程式現在正在報告您當前的Windows使用者名稱,對嗎?

哇,等等,您還沒有完成!

我們已經接近了,但是如果我們想在React中使用它,我們還有很多工作要做。CORS。別說了。什麼是CORS? CORS是跨域資源共享 ,這是您要允許[this]訪問[that]的一種非常真實的方式。在現實世界中,預設情況下將瀏覽器配置為禁止通過指令碼發起的HTTP請求,除非接收端明確允許。

所以……讓我們做一下,這樣這個API可以接受來自React應用程式的CORS請求,對吧?我們不會在這裡變得很花哨。為了使此操作生效,我們需要在中新增一些設定, appsettings.json 然後在中進行其他設定 Startup.cs

appsettings.json

首先,我們去 appsettings.json 新增以下內容: "CorsOrigins": [ "http://localhost:3000" ], 。是的,它是陣列型別。為什麼?主要是因為它為您提供了選擇。假設在開發中,我打開了通往機器的通道,以便某人(甚至我)可以從另一臺裝置訪問該應用程式。顯然,它們不會通過本地主機名或IP地址連線到localhost。所有這些都是我可能要設定的選項。

啟動檔案

接下來,我們需要 將CORS新增 到我們的服務和中介軟體中。在ConfigureServices中,請新增以下程式碼:

// add this class somewhere outside of the Startup class
public class Constants
{
public const string CORS_ORIGINS = "CorsOrigins";
}

services.AddCors(opt =>
{
opt.AddPolicy("CorsPolicy", builder => builder
        .AllowAnyHeader()
        .AllowAnyMethod()
        .WithOrigins(Configuration.GetSection(Constants.CORS_ORIGINS).Get<string[]>())
        .AllowCredentials());
});

此程式碼的簡要說明。它允許傳遞任何標頭,使用任何http方法(GET,POST,PUT,DELETE等),必須來自配置中特定的來源之一,並允許在標頭中傳遞憑據。在您自己的應用程式中,您可以更改許多設定。您可能不會更改其中任何一個。至少您現在知道它們了。

接下來,我們需要新增 app.UseCors("CorsPolicy") 到我們的 Configure(app, env) 方法中。請注意,這是中介軟體和 中介軟體順序 。在這種情況下,它需要跟從 app.UseRouting() 但在此之前 app.UseAuthentication() app.UseAuthorization() 。順便說一句,如果您添加了中介軟體,但中介軟體工作不正常,則應檢查其註冊順序。

現在使用app.UseCors(“ CorsPolicy”)配置方法

現在我們準備好讓我們的React應用程式與Windows身份驗證掛鉤了!

帶有React的Windows身份驗證–連線起來!

對此感到興奮嗎?我知道我是。這比您想象的要容易。準備好了嗎?

您需要做的就是在 fetch 請求中新增兩個屬性: credentials: "include" mode: 'cors'

將會發生的情況是,如果您訪問的站點與您不在同一域(或計算機)上,則瀏覽器將提示您輸入該Active Directory,LDAP或計算機例項的憑據。成功進行身份驗證後,瀏覽器將其儲存以備將來使用。如果您在完全相同的計算機或域上,則不會提示憑據。

話雖如此,您可以(並且可能應該)設定服務f呼叫的方式,因此不必在各處都輸入相同的垃圾。當我第一次開始使用React時,我很快意識到,設定一些獲取幫助程式來啟動它比較容易。我意識到的第二件事是,在React程式碼中將所有.NET API控制器與“服務”進行匹配更加容易。

考慮到這一點,讓我們看一下我的提取幫助器和示例服務。

fetch-helpers.js

這是我在此測試應用程式中擁有的一些基礎知識:

export const handleResponse = (response) => {
  return response.text().then((text) => {
    const data = text && JSON.parse(text);
    if (!response.ok) {
      const error = (data && data) || response.statusText;
      return Promise.reject(error);
    }

    return data;
  });
};

export const requestBase = (() => {
  if (typeof window !== "undefined") {
    return {
      method: "POST",
      credentials: "include",
      mode: 'cors',
      headers: new Headers({
        Accept: "application/json",
        "Content-Type": "application/json",
      }),
    };
  } else
    return {
      method: "POST",
      credentials: "include",
      // mode: 'cors',
      headers: {
        Accept: "application/json",
        "Content-Type": "application/json",
      },
    };
})();

值得注意的是, requestBase 如果它不是NodeJS(經過渲染的React),則可以具有不同的返回物件。很難發現差異,但是無瀏覽器版本將標頭設定為Headers物件的例項,而瀏覽器版本僅設定JSON物件。

weather-api.js

接下來,讓我們看看我們的服務如何使用fetch-helpers。首先,下面的apiBase是完整的URL。顯然,您不會這樣做,但實際上會將React的baseUrl設定為更高的級別(通常在HTML級別)。這是示例程式碼,請加一點鹽。

import { handleResponse, requestBase } from "../_helpers";

const apiBase = "http://localhost:44387/weatherforecast";

class WeatherForecastService {
  getForecasts() {
    let request = Object.assign({}, requestBase, { method: "GET" });
    let url = `${apiBase}`;

    return fetch(url, request).then(handleResponse);
  }

  getProtectedForecast() {
    let request = Object.assign({}, requestBase, { method: "GET" });
    let url = `${apiBase}/5`;

    return fetch(url, request).then(handleResponse);
  }
}

const instance = Object.freeze(new WeatherForecastService());
export { instance as WeatherForecastService };

我在這裡所做的只是公開我希望React可以訪問的方法,將它們包裝在 baseRequest from的周圍,並 fetch-helpers 使用我的 handleResponse from 來處理響應 fetch-helpers ,然後傳遞迴呼叫方。

最後但同樣重要的是,將其連線起來!

現在我們已經鋪設了所有管道,現在該連線所有東西了。我只是直接編輯App.js。告我。對於該示例,我將匯入所有三個API服務檔案,然後 const 為我要處理的每個按鈕設定一些功能。其中的每一個都僅登出到控制檯,而不用花費大量精力。最後,當然是按鈕本身。由於該檔案在修改後相當龐大,因此我僅摘錄了按鈕事件之一以及呼叫它的React按鈕元件本身。

const getProtectedForecast = () => {
console.log("attempting...");

WeatherForecastService.getProtectedForecast()
  .then((response) => {
console.log("response: ", JSON.stringify(response));
console.log("oh boy!");
  })
  .catch((err) => {
console.error(err);
  });
};

<button type="button" onClick={getProtectedForecast}>
Get protected forecast
</button>

重要環節– Windows身份驗證基於角色的安全性

雖然上述設定是非常基本的,但它缺少一個非常重要的部分,不是嗎?基於角色的安全性。在這裡考慮一下此內容,這是對第2部分的非常簡短的介紹,而我將在不久的將來寫一篇文章。如果你已經點選周圍的程式碼,而閱讀這篇文章,你可能已經注意到在Startup.cs以下行:ConfigureServices: services.AddTransient(); 。如果您沒有注意到它,那是可以的,因為我現在將談論它。

在對使用者進行身份驗證之後但在獲得授權之前,授權提供者將呼叫您的自定義 IClaimsTransformation 實現(如果提供)。 參見MSDN 。在我的實現中(位於中 ClaimsTransformer ),您將看到我只是在隨意新增角色“ Super-awesome”作為我們 WindowsIdentity 用於其宣告型別的相同宣告型別。

IClaimsTransformation的ClaimsTransformer實現者

接下來,您可能已經注意到 WeatherForecastController 我添加了一個 GetAnother 方法呼叫,該方法呼叫需要“超級棒”角色。在此之下,我還添加了一種 GetFail 方法,該方法將始終失敗,因為使用者不在“管理員”角色中。這些方法都沒有連線到React應用程式中,並且要求您直接在瀏覽器中單擊它們以檢視它們是否有效(或可能無效)。

基於Controller動作的基於角色的Windows身份驗證

結論

將Windows身份驗證連線到您的React應用程式並不困難。這也是非常基本的。交鑰匙解決方案可以使您走得更快,更遠,但是如果您沒有基礎設施或現金,仍然可以自己動手做。和往常一樣,我部落格文章中的程式碼可以在 GitHub 找到