vlambda博客
学习文章列表

基于REACT和.NET CORE集成WINDOWS身份验证

有很多方法可以向您的应用程序添加身份验证。虽然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。


基于REACT和.NET CORE集成WINDOWS身份验证

切换默认启动

接下来,我需要打开我的launchSettings.json"windowsAuthentication": trueiisSettings下进行设置。

启用Windows身份验证

基于REACT和.NET CORE集成WINDOWS身份验证

好吧,让我们稍等一秒钟。为了使Windows身份验证起作用,您将需要在IIS或IIS Express中进行托管。您也可以使用KestrelHTTP.sys托管来完成此操作,但出于本文的方便,让我们集中讨论IIS Express。如果您想使它在Docker和/或Linux上运行,您将要使用Kestrel。

我们的应用程序现在可以与Windows身份验证一起使用了,但是如果我们现在启动它,它仍然不会使用。我们还有很多工作要做。

配置Windows身份验证

现在我们已经设置了API以通过IIS使用Windows身份验证,我们需要使API本身意识到这一点。为此,我们需要对进行一些调整Startup.csMSDN上有文档,但我们也可以在这里进行阅读。

您需要做的第一件事是services.AddAuthentication(IISDefaults.AuthenticationScheme);在您的ConfigureServices方法中添加任何位置。这利用了Microsoft.AspNetCore.Server.IISIntegration命名空间。

基于REACT和.NET CORE集成WINDOWS身份验证

添加Windows身份验证

接下来,您需要配置Windows身份验证需要保护哪些控制器或动作。顺便说一下,这就是为什么我们"anonymousAuthentication": true独自留在launchSettings.json。这里的一个用例是,如果您使用的是Swagger,并且希望匿名访问文档并且仅保护API本身。

在我的用例中,我假装我希望所有API控制器都需要身份验证。鉴于此,我创建了一个WebControllerBase.cs,并用[Authorize]属性对其进行了装饰。请注意,您可以改为用[Authorize]标记每个控制器。就是说,我的理由实际上是押韵的,在以后的第二部分中,我将进一步阐述这个想法。现在,只需滚动即可。

WebControllerBase,将[Authorize]标记为需要身份验证

接下来,只需让我们现有的控制器和新控制器继承自WebControllerBase而不是即可ControllerBase

基于REACT和.NET CORE集成WINDOWS身份验证

现在,我们现有的控制器继承自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

启动文件

接下来,我们需要将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()。顺便说一句,如果您添加了中间件,但中间件工作不正常,则应检查其注册顺序。

基于REACT和.NET CORE集成WINDOWS身份验证


现在使用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 = "https://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可以访问的方法,将它们包装在baseRequestfrom的周围,并fetch-helpers使用我的handleResponsefrom 来处理响应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>

基于REACT和.NET CORE集成WINDOWS身份验证

重要环节– Windows身份验证基于角色的安全性

虽然上述设置是非常基本的,但它缺少一个非常重要的部分,不是吗?基于角色的安全性。在这里考虑一下此内容,这是对第2部分的非常简短的介绍,而我将在不久的将来写一篇文章。如果你已经点击周围的代码,而阅读这篇文章,你可能已经注意到在Startup.cs以下行:ConfigureServices:services.AddTransient();。如果您没有注意到它,那是可以的,因为我现在将谈论它。

在对用户进行身份验证之后但在获得授权之前,授权提供者将调用您的自定义IClaimsTransformation实现(如果提供)。参见MSDN。在我的实现中(位于中ClaimsTransformer),您将看到我只是在随意添加角色“ Super-awesome”作为我们WindowsIdentity用于其声明类型的相同声明类型。

IClaimsTransformation的ClaimsTransformer实现者

接下来,您可能已经注意到WeatherForecastController我添加了一个GetAnother方法调用,该方法调用需要“超级棒”角色。在此之下,我还添加了一种GetFail方法,该方法将始终失败,因为用户不在“管理员”角色中。这些方法都没有连接到React应用程序中,并且要求您直接在浏览器中单击它们以查看它们是否有效(或可能无效)。

基于Controller动作的基于角色的Windows身份验证

结论

将Windows身份验证连接到您的React应用程序并不困难。这也是非常基本的。交钥匙解决方案可以使您走得更快,更远,但是如果您没有基础设施或现金,仍然可以自己动手做。和往常一样,我博客文章中的代码可以在GitHub找到