九、ASP.NET Core 的安全防护

正确保护您的 api 是必要的。 在本章中,我们将学习如何保护 ASP.NET Core 5 Web API 应用,因为这也是开发人员的责任,他们构建后端应用来保护 API。 . net 中有两个可用的框架可以用来保护应用,还有云身份提供商。

我们将解决以下问题:

  • 了解 ASP.NET Core 身份
  • 引入 IdentityServer4
  • 客户身份与访问管理(CIAM)
  • 验证使用 JWT

技术要求

以下是你完成本章所需要的东西:

  • Visual Studio 2019, Visual Studio for Mac,或 Rider
  • 邮递员
  • JWT 调试器:https://jwt.io

这里是本章已完成的回购的链接:https://github.com/PacktPublishing/ASP.NET-Core-and-Vue.js/tree/master/Chapter09

【课文讲解】 NET Core 身份

NET Core Identity是。NET 中的一个开源身份框架。 它为。net 应用提供了一种实现认证的方法,以确定某人的身份和授权,从而详细说明对用户访问的限制。 它还允许。net 应用管理用户帐户。 建议在应用中使用 Identity,以便您的应用遵循安全最佳实践和注意事项。 ASP.NET Core 还节省了开发时间,因此您可以只专注于业务逻辑。

NET Core 标识特性

ASP. net 的主要特点 NET Core 身份提供如下:

  • 用户帐号和密码管理。
  • 多因素认证(MFA)和双因素认证(2FA)。
  • 登录和注销功能。
  • 外部认证提供者,如 Facebook、谷歌、Twitter 和 Microsoft 帐户。
  • 根据需要定制身份框架。

开发人员需要了解认证在 ASP 中是如何流动的.NET Core 身份及其工作原理。 ASP.NET Core 身份使用基于声明的安全方案,该方案早在。NET 版本 4.5 中就引入了。

声明是身份的属性,它包含一个键值对。 一个身份可以有一个或多个与之相关的声明。 例如,学校应用中的学生用户可以有多个声明来定义他们是谁。 这些声明可以包括namesschoolEmailsdateOfBirths。 每一项要求都必须有其独特的价值。

ClaimsIdentity类代表一个身份及其所有要求。 ASP 中的用户.NET Core 应用可以通过ClaimsPrincipal类的帮助拥有多个标识。 ClaimsPrincipal包含一个或多个ClaimsIdentity类; 因此,它可以有一个或多个类 ims。

为了更好地概述ClaimsIdentityClaimsPrincipal,让我给你看一个图表,图 9.1如下:

Figure 9.1 – Two forms of identification

图 9.1 -识别离子的两种形式

为了说明这一点,请参见前面的图 9.1,其中学生可能有两种形式的身份证明:他们的学生证和他们的驾驶证。 学生证上有学生号和学校名称。 他们的驾照上有身份证和出生日期。 每个标识都足够灵活,允许访问不同的资源。 ClaimsPrincipal从相关的身份继承索赔列表。 在这个场景中,驾照索赔和学生证索赔识别学生用户。

Cookie 认证是 ASP 中最常见的认证方式.NET Core 应用。 很高兴知道 ASP.NET Core 内置了中间件,使应用能够利用基于 cookie 的认证。

另外,为了更好地概述 cookie 认证,让我向您展示一个图表图 9.2,如下图:

Figure 9.2 – ASP.NET Core  middleware

图 9.2 - ASP.NET Core cookie middleware

前面的图概述了一个简单的 ASP.NET Core cookie 中间件工作。 下面的步骤是在引擎盖下发生的:

  1. ClaimsPrincipal及其继承的所有索赔在用户成功登录后被序列化到 cookie 中。
  2. 浏览器从服务器保存加密的 cookie。
  3. cookie 通过请求头发送,并在每个下一个请求上由服务器验证。
  4. 在成功验证了ClaimsPrincipal之后,将使用该 cookie 重新创建它。 然后主体将 att 发送到 HTTP 上下文用户属性。
  5. 最后,应用代码可以访问链接到主体的所有用户信息和索赔。

这是对 ASP 的快速介绍.NET Core 身份; 现在,让我们继续访问 IdentityServer4。

引入 IdentityServer4

安全令牌服务(STS)是基于令牌认证的关键组件。 您可能遇到的其他术语是身份提供者或授权服务器。 它是一个软件,用于生成并向客户端应用发出安全令牌。 客户端应用将用户验证重定向到 STS 来处理它。 对令牌进行了加密和签名,以确保令牌免受任何篡改。

应用中的加密使用令牌服务保存的私钥,而用于解密令牌的公钥则与客户端应用共享。 通过这种方式,客户端应用相信令牌来自正确的令牌服务。 用于提供身份信息的标准,如 OpenID Connect,由令牌服务使用。

当谈到消费令牌服务时,有不同的选项。 您可以使用预构建的服务(如活动目录联合服务(ADFS)来构建使用 IdentityServer 框架的自定义服务。

IdentityServer4是一个用于令牌认证的开源框架。 在编写本文时,IdentityServer4 是 ASP 的最新推荐版本.NET Core 5 应用:

Figure 9.3 – Basic structure of a security system

图 9.3 -安全系统的基本结构

前面的图解释了安全系统的基本结构。 客户机是一个从 IdentityServer4 请求令牌的应用。 例如,它可以是一个移动应用或网页应用。 IdentityServer4 必须通过注册或将它们包含在实体或存储集合中来了解允许使用它的客户机应用。 该集合可以持久化或保存在数据库或内存中。

接下来是使用客户端应用或与客户端应用交互的最终用户。 用户还必须通过 IdentityServer 注册。 唯一标识符以及用户名和密码凭证是定义用户的身份数据的示例。 用户还可以拥有一个或多个关联的索赔。 身份资源和 API 资源是需要保护的东西。 身份资源是关于身份的详细信息,例如用户声明。 另一方面,API 资源表示受保护的功能,比如 Web API。 资源也必须注册; t 它们需要在标识服务器存储中注册。 两种类型的令牌和两种资源类型一起生成。

当 IdentityServer4 对用户进行认证时,信息与身份令牌一起发送。 客户端将访问令牌转发给 API,当请求访问 API 资源时,API 允许或授予对受保护功能或数据的访问,并且成功发出访问令牌。

IdentityServer4 是一个中间件; 它使用行业标准JSON Web 令牌(jwt),并实现了两个标准协议 OAuth 2 和 OpenID Connect。 OAuth 2为授权的开放标准; 它确保用户具有访问受保护资源的权限。 另一方面,OpenID Connect是一个认证协议,是建立在 OAuth 2 协议之上的扩展。 使用 OAuth 2,客户端可以从 STS 请求访问令牌,然后与 api 通信。

IdentityServer4 可以通过其框架和 OAuth 2 和 OpenID Connect 的实现来管理或处理认证和授权需求。

以上就是对 IdentityServer4 的简要介绍; 让我们进入CIAM

客户身份与访问管理(CIAM)

安全性是复杂的,您必须正确对待它。 所以今天,开发人员相信认证提供者可以帮助保护他们的应用。 Auth0,AAD B2C,Okta、【显示】AWS 隐身,和GCP 身份平台是很受欢迎的认证提供者,也称为【病人】身份作为服务(IDaaS)。 那么,为什么要考虑使用认证提供者?

注意:

我现在是 Auth0 大使。 不,我不是公司的雇员,也得不到任何金钱补偿。 尽管我偶尔也会在会议上发言并提及这些东西时得到一些好处和其他很酷的待遇。 但是我成为 Auth0 大使,正是因为我一直在使用和推荐它。

首先,安全性是复杂的,而且很容易出错。 这就像你在一个巨大的谜题中获得了所有正确的选择。 通过选择专门从事安全的云身份认证提供商,您可以相信认证提供商的安全工程师知道他们在做什么,并且可以把时间集中在开发应用上。 类似地,您可以根据自己的需要定制提供者。 因此,它们可以很好地与您的应用集成。 那么,定价呢? 嗯,价格各不相同,但一些认证提供商提供了一个免费层,适用于许多类型的应用。

我建议您使用 Auth0,因为它很容易使用,是最流行的,并且有优秀的在线文档。 它还为创建单页应用提供了优秀的库和 sdk,而且价格合理,因为它提供了慷慨的免费层。 Auth0 的免费定价支持多达 7000 名活跃用户。 如果你想知道付费版有什么功能,可以去他们的网站看看,但免费版对大多数应用来说已经足够了。

术语iaas云身份提供商正在被缩写 CIAM 所取代。

好的,那么什么是CIAM,或者客户身份和访问管理?

CIAM 处于客户体验、安全性和分析的交叉点。 为用户提供无障碍的登陆方式对于建立用户忠诚度和推动转化率至关重要。 采取措施防止数据泄露和保护敏感数据免受恶意入侵是 CIAM 遵循数据隐私法律和安全政策的重点。 更不用说将用户数据编译成一个或单个的真相源对于理解您的客户是至关重要的。

许多公司选择使用第三方 IDaaS,而不是在内部从头构建解决方案。

公司需要身份和访问管理(IAM)解决方案,以满足不同类型的终端用户:客户、员工和企业客户。 但是每种类型的用户对用户体验(用户体验)和安全性的要求不同。 因此,CIAM 解决方案提供了一组出色的特性,有别于劳动力标识解决方案或 B2B。

以下是组成现代 CIAM 解决方案的四个特性。 没有两个 CIAM 解决方案提供完全相同的功能,但如果您正在注册或购买一个 CIAM 平台,它应该具有以下标准:

  • 可扩展性:CIAM 必须将从数千用户扩展到数百万甚至数十亿用户。
  • 单点登录:允许用户登录或登录到一个应用,然后自动登录到一组不同的应用。
  • 多因素认证:MFA 是一种更安全的验证用户身份的方法,而不是普通的用户名和密码组合。
  • 集中式用户管理:您对终端用户或客户的洞察力可以成为一个极好的竞争优势,但前提是数据是可访问的有组织且准确。

以上就是对 CIAM 的介绍。 现在,让我们在下一节讨论应用中的安全性实现。

使用 JWT 的认证实现

JWTJSON Web 令牌是一种类型的令牌,用于在机器之间携带身份数据。 它由不同的编程语言、行业标准支持,并且可以很容易地传递。 JWT 是自包含的,并且它在自身内保存所需的标识信息,如下图所示:

Figure 9.4 – Parts of a JWT

图 9.4 - JWT 的部分

前面的图 9.4显示了 JWT 的三个部分:头、负载和签名。 头文件有两个属性。 一个是alg,它是算法的缩写,它确定用于编码此令牌的算法。 typ等于JWT。 我们不需要担心这个头,因为这只是一个标准。

对我们来说重要的是第二部分,即有效载荷。 所以在这里,我们有一个 JSON 对象,它有三个属性:sub(通常是一个用户 ID)、nameiat(这是生成令牌的时间)。 这里您需要知道的是,有效负载包含关于用户的公共属性,比如您的护照上如何拥有关于您自己的一些属性,比如姓名、出生日期和出生地。 我们在 JWT 上有相同的概念。 我们可以包含一些关于用户的基本公共属性。 因此,每次我们从客户机向服务器发送令牌时,我们都可以轻松地从有效负载中提取用户 ID。 如果您需要知道用户的名称,您也可以简单地从有效负载中提取它,这意味着您不需要查询数据库,发送用户 ID 来获取用户对象,然后提取 name 属性。

JWT 的第三部分是数字签名。 数字签名是基于 JWT 的头和有效负载以及秘密或私钥创建的。 如前所述,密钥或私钥仅在服务器上可用。

为了更好的可读性,也请参考 GitHub 中本章的源代码。 如果恶意用户获取了 JWT 并修改了属性,数字签名将无效,因为 JWT 内容被修改了。 现在黑客需要一个新的数字签名。 然而,黑客无法生成数字签名,因为他们需要只有在服务器上可用的私钥。 因此,如果他们不能访问服务器,他们就不能创建有效的数字签名。 当他们将这个新的篡改的 JWT 发送到服务器时,服务器将拒绝它。 服务器会说“不是有效的 JWT”。

这就是 JWT 的工作方式。

实现基于令牌的认证

我们将在应用中实现使用 JWT 的基于令牌的认证。 这个实现生成了一个 JWT,但是我需要您从本章中学到的是,了解您需要什么样的体系结构,并规划在未来的应用中适合使用什么样的安全实现类型。 您可能需要 ASP.NET Core 标识、IdentityServer4 或 CIAM。

所以,让我们开始为我们的应用添加安全性:

  1. Create a new .NET 5 class library inside the infrastructure directory and name it Travel.Identity. After creating the library, delete the default Class1.cs file that you will find there.

    我们还需要创建引用。 创建一个从这个项目Travel.IdentityTravel.Application项目的引用,从Travel.WebApi项目到这个项目的引用。

    然后在Travel.Identity项目中安装以下两个 NuGet 包:

    Microsoft.AspNetCore.Authentication.JwtBearer

    这个 NuGet 包是一个中间件,它允许。net 应用接收 OpenID Connect 令牌。 下面的 NuGet 包是一个中间件,它允许。net 应用支持 OpenID 认证工作流:

    Microsoft.AspNetCore.Authentication.OpenIdConnect

    上述 NuGet 包目前安装在Travel.WebApi项目中,但我们需要在Travel.Identity项目中安装,而不在Travel.WebApi项目中。 也就是说,删除安装在Travel.WebApi项目中的JwtBearerOpenIdConnect包。

  2. Next, we go to the Travel.Domain project and create a new C# file with the name User.cs inside the Entities folder:

    namespace Travel.Domain.Entities { public class User { public int Id { get; set; } public string FirstName { get; set; } public string LastName { get; set; } public string Username { get; set; } public string Password { get; set; } } }

    前面的代码块是一个简单的包含IdFirstNameLastNameUsernamePasswordUser实体。

  3. Now, let's go back to the Travel.Identity project and create a folder in its root directory. Name the folder Helpers. Create three C# files inside the Helpers folder and name them AuthSettings.cs, JwtMiddleware.cs, and AuthorizeAttribute.cs, like so:

    namespace Travel.Identity.Helpers { public class AuthSettings { public string Secret { get; set; } } }

    前面的代码块用于稍后将在appsettings.json文件中编写的秘密配置。

    以下来自JwtMiddleware.cs的代码块是自定义中间件,用于检测和提取 HTTP 请求中的授权报头:

    … using System.IdentityModel.Tokens.Jwt; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Options; using Microsoft.IdentityModel.Tokens; using Travel.Application.Common.Interfaces; namespace Travel.Identity.Helpers { public class JwtMiddleware { private readonly RequestDelegate _next; private readonly AuthSettings _authSettings; public JwtMiddleware(RequestDelegate next, IOptions<AuthSettings> appSettings) { … } public async Task Invoke(HttpContext context, IUserService userService) { … } private void AttachUserToContext(HttpContext context, IUserService userService, string token) { … } catch { } } } }

    前面的代码块还验证了从 sender 应用的头中提取的令牌,我将其截断并移动到这里:

    ``` private void AttachUserToContext(HttpContext context, IUserService userService, string token) { try { var tokenHandler = new JwtSecurityTokenHandler(); byte[] key = Encoding.ASCII.GetBytes (_authSettings.Secret); tokenHandler.ValidateToken(token, new TokenValidationParameters { ValidateIssuerSigningKey = true, IssuerSigningKey = new SymmetricSecurityKey(key), ValidateIssuer = false, ValidateAudience = false, ClockSkew = TimeSpan.Zero }, out var validatedToken); var jwtToken = (JwtSecurityToken)validatedToken; var userId = int.Parse (jwtToken.Claims.First(c => c.Type == "id").Value);

                context.Items["User"] = 
                  userService.GetById(userId);
            } catch { }
        }
    

    ```

    假设验证是正确的; 调用userService.GetById来获取用户的数据。

    下面来自AuthorizeAttribute.cs的代码块是一个自定义属性,允许你用Authorize注释类或方法:

    … namespace Travel.Identity.Helpers { [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)] public class AuthorizeAttribute : Attribute, IAuthorizationFilter { public void OnAuthorization (AuthorizationFilterContext context) { var user = (User)context.HttpContext.Items["User"]; if (user == null) { context.Result = new JsonResult(new { message = "Unauthorized" }) { StatusCode = StatusCodes.Status401Unauthorized }; } } } }

    如果命中带注释路由或控制器的请求没有经过认证,控制器将发送一个带有Unauthorized消息的 JSON 对象。

  4. Now let's go to the Travel.Application project. Find the Dtos directory, create a folder inside of that directory, and name it User. Then create two C# files inside of the User folder and name them AuthenticateResponse.cs and AuthenticateRequest.cs, like so:

    namespace Travel.Application.Dtos.User { public class AuthenticateResponse { public int Id { get; set; } public string FirstName { get; set; } public string LastName { get; set; } public string Username { get; set; } public string Token { get; set; } public AuthenticateResponse (Domain.Entities.User user, string token) { Id = user.Id; FirstName = user.FirstName; LastName = user.LastName; Username = user.Username; Token = token; } } }

    这个User模型是经过验证的用户成功登录后响应的形状:

    using System.ComponentModel.DataAnnotations; namespace Travel.Application.Dtos.User { public class AuthenticateRequest { [Required] public string Username { get; set; } [Required] public string Password { get; set; } } }

    前面的代码块要求登录请求在请求体中具有UsernamePassword属性或键。

  5. Now let's create an interface for the user service we will create later. Look for the Common directory inside of the Travel.Application project. Now there's the Interfaces directory inside the Common directory; create an interface inside the Interfaces folder and name it IUserService.cs like so:

    using Travel.Application.Dtos.User; using Travel.Domain.Entities; namespace Travel.Application.Common.Interfaces { public interface IUserService { AuthenticateResponse Authenticate (AuthenticateRequest model); User GetById(int id); } }

    在创建IUserService.cs文件之后,让我们创建该接口的实现。 回到Travel.Identity项目并在项目根目录中创建一个Services文件夹。 然后创建一个新的 c#文件,命名为UserService.cs:

    … using System.IdentityModel.Tokens.Jwt; using System.Security.Claims; using Microsoft.Extensions.Options; using Microsoft.IdentityModel.Tokens; namespace Travel.Identity.Services { public class UserService : IUserService { private readonly List<User> _users = new List<User> { new User {…} }; … public AuthenticateResponse Authenticate (AuthenticateRequest model) { … } public User GetById(int id) => _users.FirstOrDefault(u => u.Id == id); private string GenerateJwtToken(User user) { … } } }

    前面的代码块是我们前面创建的IUserService.cs的实现。 出于可读性的考虑,我截断了AuthenticateGeneratateJwtToken方法的业务逻辑。 下面是Authenticate方法和GenerateJwtToken方法的完整代码:

    public AuthenticateResponse Authenticate (AuthenticateRequest model) { var user = _users.SingleOrDefault(u => u.Username == model.Username && u.Password == model.Password); if (user == null) return null; var token = GenerateJwtToken(user); return new AuthenticateResponse(user, token); }

    此实现通过检查用户是否在数据库中存在来验证User。 但是,为了简化实现,我们在这里对user对象进行了硬编码:

    private string GenerateJwtToken(User user) { byte[] key = Encoding.ASCII.GetBytes (_authSettings.Secret); var tokenDescriptor = new SecurityTokenDescriptor { Subject = new ClaimsIdentity(new[] { new Claim("id", user.Id.ToString()) }), Expires = DateTime.UtcNow.AddDays(1), SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature ) }; var tokenHandler = new JwtSecurityTokenHandler(); var token = tokenHandler.CreateToken (tokenDescriptor); return tokenHandler.WriteToken(token); }

    GenerateJwtToken方法使用appsettings.json文件中的Secret值生成一个 JWT。 它还使用JwtSecurityTokenHandlerSecurityTokenDescriptor,它们创建令牌的声明或有效负载。 您将注意到,在这里SecurityTokenDescriptor使用密钥和算法参数调用SigningCredentials来签名令牌。

  6. Now let's create a DependencyInjection class that uses a static IServiceCollection for the Travel.Identity project. Create a C# class in the root directory of the Travel.Identity project like so:

    ``` using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Travel.Application.Common.Interfaces; using Travel.Identity.Helpers; using Travel.Identity.Services; namespace Travel.Identity { public static class DependencyInjection { public static IServiceCollection AddInfrastructureIdentity (this IServiceCollection services, IConfiguration config) { services.Configure (config.GetSection(nameof(AuthSettings))); services.AddScoped();

      return services;
    }
    

    } } ```

    下面是我们将要在Startup.cs中注册的依赖注入(DI)容器中的已注册服务。 我们正在获取appsettings.json文件中的AuthSettings对象,并为UserService注册一个有作用域的生命周期。 AuthSettingsSecret键是一个敏感字符串,必须存储在环境变量或 Azure key Vault 中以保护它。

  7. Next, we create a C# file and name it UsersController inside the Travel.WebApi project, specifically inside the v1 folder of the Controllers directory, like so:

    using Microsoft.AspNetCore.Mvc; using Travel.Application.Common.Interfaces; using Travel.Application.Dtos.User; namespace Travel.WebApi.Controllers.v1 { [ApiVersion("1.0")] [ApiController] [Route("api/v{version:apiVersion}/[controller]")] public class UsersController : ControllerBase { private readonly IUserService _userService; public UsersController(IUserService userService) => _userService = userService; [HttpPost("auth")] public IActionResult Authenticate([FromBody] AuthenticateRequest model) { var response = _userService.Authenticate(model); if (response == null) return BadRequest(new { message = "Username or password is incorrect" }); return Ok(response); } } }

    这个控制器不需要授权,因为它是用于登录的。 它还使用了v1API 来保持一致性。 控制器对用户进行认证,并使用User的 JWT 进行响应。

  8. Now, let's protect the rest of the controllers by adding the custom attribute in ApiController. Let's update ApiController's code:

    namespace Travel.WebApi.Controllers.v1 { [Authorize] [ApiVersion("1.0")] [ApiController] [Route("api/v{version:apiVersion}/[controller]")] public abstract class ApiController : ControllerBase { private IMediator _mediator; protected IMediator Mediator => _mediator ??= HttpContext.RequestServices.GetService <IMediator>(); } }

    我们用Authorize属性注释ApiController,这是我们先前创建的自定义属性。 该属性保护ApiController的所有子类不受经过认证的消费者或用户的影响。

  9. Now the last file to update is Startup.cs with the following code:

    public void ConfigureServices(IServiceCollection services) { services.AddApplication(); services.AddInfrastructureData(); services.AddInfrastructureShared (Configuration); services.AddInfrastructureIdentity (Configuration); services.AddHttpContextAccessor(); services.AddControllers(); … }

    services.AddInfrastructureShared(Configuration);方法下面添加services.AddInfrastructureIdentity(Configuration);方法。

  10. Let's also update the SwaggerGen configuration. The code that we are adding will allow us to enter a JWT in the Swagger HTTP requests header through a dialog box:

    services.AddSwaggerGen(c => { c.OperationFilter <SwaggerDefaultValues>(); c.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme { Description = "JWT Authorization header using the Bearer scheme.", Name = "Authorization", In = ParameterLocation.Header, Type = SecuritySchemeType.Http, Scheme = "bearer" }); c.AddSecurityRequirement(new OpenApiSecurityRequirement { { new OpenApiSecurityScheme { Reference = new OpenApiReference { Type = ReferenceType.SecurityScheme, Id = "Bearer" } }, new List<string>() } }); });

    AddSecurityDefinition描述了 API 是如何被保护的,而AddSecurityRequirement在 API 中添加了全局安全需求。

    另外,像这样更新中间件:

    public void Configure(IApplicationBuilder app, IWebHostEnvironment env, IApiVersionDescriptionProvider provider) { … app.UseRouting(); app.UseMiddleware<JwtMiddleware>(); app.UseAuthorization(); … }

    app.UserRouting()app.UseAuthorization()之间插入定制app.UseMiddleware<JwtMiddleware>()中间件。 这个代码块是我们之前创建的自定义中间件。

    现在,Travel.Identity项目被集成到Travel.WebApi项目中。

  11. 运行应用,看看安全性是否在应用和 Swagger 中正常工作。

在运行应用之后,让我们检查 Swagger UI。

检查 Swagger UI

在设置了 JWT 认证并将其添加到 Swagger 后,让我们看看如何在 Swagger UI 中使用和测试受保护的路由:

  1. Swagger UI has the Authorize button for adding credentials or JWT, as shown in the following figure, Figure 9.5:

    Figure 9.5 – OpenAPI with the security definition and requirement

    图 9.5 - OpenAPI 的安全定义和需求

  2. Now let's check out the /api/v1/Users/auth route of the Users controller:

    Figure 9.6 – Signing in using Swagger UI

    图 9.6 -使用 Swagger UI 登录

    我们可以通过发送一个以usernameyoursuperheropasswordPass123!POST请求来测试 auth API,如上图所示。

  3. Now let's check out the response of the API after sending the POST request:

    Figure 9.7 – JWT response after successful login

    图 9.7 -登录成功后的 JWT 响应

    前面的屏幕截图显示了来自 auth API 响应体的 JWT,这意味着我们在发送用户名和密码后进行了认证。 让我们复制 token 的值,因为我们将在 Swagger UI 的Authorize框中使用它。

  4. Go to the https://jwt.io website and paste the JWT in the left box, as shown in the following screenshot:

    Figure 9.8 – Encoded and decoded JWT

    图 9.8 -编码和解码的 JWT

    我们看到从应用的 auth API 接收到的已编码的 JWT。 同时,右边是已解码的 JWT。 已解码的令牌显示头和有效负载。 报头显示签名或加密算法和令牌类型。 随后,解码的令牌还显示了令牌的有效负载,其中包含用户的 ID,时间之前无效,过期时间,以及时间颁发的

  5. Now let's check out the Authorize button of Swagger UI by clicking it:

    Figure 9.9 – Including the JWT in every API testing

    图 9.9 -在每个 API 测试中都包含 JWT

    将标记值粘贴到对话框中,允许 Swagger UI 在每个请求中包含Authorization头,如前面的截图所示。 您可以尝试使用 Swagger 向/api/v1.0/TourLists发送一个GET请求,也可以使用 Postman 中的 JWT。

  6. 打开 Postman 并在Auth选项卡下选择无记名令牌。 然后将标记粘贴到输入框中。 向TourLists控制器发送GET请求,https://localhost:5001/api/v1.0/TourLists:

Figure 9.10 – Authorized API testing using Postman

图 9.10 -使用 Postman 进行授权 API 测试

下面的截图显示了一个包含我们所期望数据的 200 状态码:

Figure 9.11 – 200 OK response from a protected API

图 9.11 - 200 受保护 API 的 OK 响应

如果我们发送错误的 JWT 怎么办? 你认为申请表会寄回来什么? 删除标记中的一些字母,并将它们替换为您的名字。 然后按发送按钮发送请求:

Figure 9.12 – Unauthorized request

图 9.12 -未授权请求

您将看到应用响应 401 状态码和一条Unauthorized消息,如上面的截图所示。

这就是在受保护的路由或端点中使用 Swagger UI 的方式。 现在让我们总结一下在这一章所学到的知识来结束这一章。

总结

以下是本章的摘要。 你已经学过 ASP.NET Core Identity 是。NET 中的一个开源身份框架,它为您提供了管理用户认证和帐户的能力。 它有内置的中间件,允许应用使用基于 cookie 的认证。

您已经了解到,IdentityServer4 框架消除了在您的 ASP 中实现 OAuth 2 和 OpenID Connect 的繁重工作.NET Core 应用。 IdentityServer4 的一个合理用例是,需要一个集中式认证服务器,使用基于令牌的认证对来自不同服务的请求进行认证。

您还了解到,CIAM 是一个基于云的身份提供商,它为公司提供分析、安全性和良好的客户体验。 它具有可伸缩性,具有集中式用户管理,并且易于设置单点登录和 MFA。

你已经学习了如何保护 ASP。 使用 JWT 的 NET Core 5 应用。 应用用一个令牌响应一个经过认证的请求,令牌包含一个用户信息的有效负载。

最后,您还学习了如何在 Swagger UI 中显示受保护的 api,以及如何在 Swagger 的每个请求中包含授权头。

在下一章中,我们将讨论使用 Redis 缓存来提高性能。