这是有关ASP.NET Core中的身份验证和授权的一系列文章中的第一篇。在本文中,我将大致讨论身份验证以及ASP.NET Core中基于声明的身份验证的工作方式。
身份验证和授权之间的区别
首先,我们应该弄清这两个依赖的安全方面之间的区别。简单的答案是,认证是确定的过程中你是谁,而授权围绕着你被允许做什么,即权限。显然,在确定允许用户做什么之前,您需要知道他们是谁,因此,在需要授权时,还必须首先以某种方式对用户进行身份验证。
ASP.NET Core中的身份验证
与身份相关的基本属性在ASP.NET Core中并未真正更改-尽管它们不同,但它们通常应为ASP.NET开发人员所熟悉。例如,在ASP.NET 4.x中,有一个名为User
on 的属性HttpContext
,该属性的类型为IPrincipal
,表示请求的当前用户。在ASP.NET核心有一个类似的属性命名User
,区别在于这种属性的类型的ClaimsPrincipal
,它实现了IPrincipal
。
ClaimsPrincipal
与ASP.NET 4.x相比,使用此举凸显了ASP.NET Core中身份验证工作方式的根本变化。以前,授权通常是基于角色的,因此用户可能属于一个或多个角色,并且您应用的不同部分可能要求用户具有特定角色才能访问它。在ASP.NET Core中,仍可以使用这种基于角色的授权,但这主要是出于向后兼容性的原因。他们真正希望您采用的方法是基于声明的身份验证。
基于声明的身份验证
基于声明的身份验证的概念在您初次使用时可能会有些混乱,但实际上,它可能与您已经在使用的方法非常相似。您可以将声明视为对特定身份的声明或属性。该语句包含一个名称和一个值。例如,您可能有一个DateOfBirth
索赔,FirstName
索赔,EmailAddress
索赔或IsVIP
索赔。请注意,这些陈述是关于身份或身份的,而不是他们可以做什么。
身份本身代表一个声明,该声明可能具有许多与之相关的声明。例如,考虑驾驶执照。这是一个包含了一些索赔的单一身份- ,FirstName
,,LastName
以及车辆允许您的车程。您的护照将是具有不同索偿要求的不同身份。DateOfBirth``Address
因此,让我们在ASP.NET Core的上下文中进行查看。ASP.NET Core中的身份是一个ClaimsIdentity
。该类的简化版本可能看起来像这样(实际的类要大很多!):
public class ClaimsIdentity: IIdentity
{
public string AuthenticationType { get; }
public bool IsAuthenticated { get; }
public IEnumerable<Claim> Claims { get; }
public Claim FindFirst(string type) { /*...*/ }
public Claim HasClaim(string type, string value) { /*...*/ }
}
我已经在此概述中显示了主要属性,包括Claims
由与身份相关的所有声明组成。有很多实用的方法可用于处理Claims
,我在这里展示了其中的两种。这些在您进行授权时非常有用,并且您试图确定某个特定的身份是否对Claim
您感兴趣。
该AuthenticationType
物业是不言自明的。在我们以前实际的例子,这可能是字符串Passport
或DriversLicense
,但在ASP.NET它更可能是Cookies
,Bearer
或Google
等,这是仅仅是被用来验证用户,并确定与身份相关的索赔的方法。
最后,该属性IsAuthenticated
指示身份是否已通过身份验证。这似乎是多余的-如果未通过身份验证,您如何拥有声明的身份?一种情况是您允许访客用户访问您的网站,例如购物车。您仍然具有与用户关联的身份,并且该身份可能仍具有与之关联的声明,但是它们不会被认证。这是要记住的重要区别。
作为补充,在ASP.NET Core中,如果您在构造函数中创建ClaimsIdentity
并提供AuthenticationType
,IsAuthenticated
则始终为true。因此,经过身份验证的用户必须始终具有AuthenticationType
,并且,相反,您不能拥有具有的未经身份验证的用户AuthenticationType
。
多重身份
希望在这一点上您对主张及其与身份的关系有概念上的了解。我在本节的开头说过,User
on 的属性HttpContext
是a ClaimsPrincipal
,而不是a ClaimsIdentity
,因此让我们看一下它的简化版本:
public class ClaimsPrincipal :IPrincipal
{
public IIdentity Identity { get; }
public IEnumerable<ClaimsIdentity> Identities { get; }
public IEnumerable<Claim> Claims { get; }
public bool IsInRole(string role) { /*...*/ }
public Claim FindFirst(string type) { /*...*/ }
public Claim HasClaim(string type, string value) { /*...*/ }
}
从此类中获取的重要一点是,有一个Identities
返回的属性IEnumerable<ClaimsIdentity>
。因此,一个ClaimsPrincipal
可以包含多个Identities
。Identity
为了实现IPrincipal
接口,还有一个属性-在.NET Core中,它仅选择.NET Core中的第一个标识Identities
。
回到我们之前的护照和驾驶执照示例,实际上可以使用多种身份-这些文件都是身份的形式,每种形式都包含许多索偿要求。在这种情况下,您是主体,并且您有两种形式的身份。当您拥有这两个身份时,作为委托人,您将继承所有身份的所有主张。
考虑另一个实际的例子-您正在乘飞机。首先,将在预订柜台要求您证明对您的索赔FirstName
,LastName
等等。幸运的是,您记得您的护照,该护照可以验证这些索赔,因此您会收到登机牌,并且正在前往下一步。
出于安全考虑,您需要证明您预订了航班。这次,您需要携带的另一种身份证明,即具有FlightNumber
要求的登机牌,因此您可以继续前进。
最后,通过安检后,您便进入了贵宾休息室,并被要求在VIP Number
索赔中证明您的贵宾身份。这可以是VIP卡的形式,这是另一种身份形式,可以验证请求的索赔。如果您没有卡,则无法提出请求的索偿,将无法访问您,因此将被要求离开并停止现场演出。

同样,这里的要点是,委托人可以具有多个身份,这些身份可以具有多个声明,并且ClaimsPrincipal
继承其的所有声明Identities
。
如前所述,基于角色的授权主要是出于向后兼容的原因,因此,IsInRole
如果您遵循ASP.NET Core中强调的基于声明的身份验证,通常将不需要该方法。在后台,这也只是使用声明实现,声明类型默认为RoleClaimType
或ClaimType.Role
。
再次考虑ASP.NET Core,可以使用多个标识和声明来保护应用程序的不同部分,就像在机场一样。例如,您可以使用用户名和密码登录,并根据与之关联的身份被授予一组声明,从而可以浏览该网站。但是请说您的应用程序中有一个特别敏感的部分,您想进一步保护它。这可能需要您提供其他身份以及其他相关声明,例如,通过使用两因素身份验证,或者要求您重新输入密码。这将允许当前原则具有多个标识,并假定所有提供的标识的主张。
创建一个新的校长
因此,现在我们已经看到了主体在ASP.NET Core中的工作方式,我们将如何实际创建主体?一个简单的示例,例如您在正常的网页登录中可能看到的,可能包含类似于以下内容的代码
public async Task<IActionResult> Login(string returnUrl = null)
{
const string Issuer = "https://gov.uk";
var claims = new List<Claim> {
new Claim(ClaimTypes.Name, "Andrew", ClaimValueTypes.String, Issuer),
new Claim(ClaimTypes.Surname, "Lock", ClaimValueTypes.String, Issuer),
new Claim(ClaimTypes.Country, "UK", ClaimValueTypes.String, Issuer),
new Claim("ChildhoodHero", "Ronnie James Dio", ClaimValueTypes.String)
};
var userIdentity = new ClaimsIdentity(claims, "Passport");
var userPrincipal = new ClaimsPrincipal(userIdentity);
await HttpContext.Authentication.SignInAsync("Cookie", userPrincipal,
new AuthenticationProperties
{
ExpiresUtc = DateTime.UtcNow.AddMinutes(20),
IsPersistent = false,
AllowRefresh = false
});
return RedirectToLocal(returnUrl);
}
该方法当前将索偿硬编码到其中,但是显然您将从数据库或其他来源获取索偿值。我们要做的第一件事是建立一个声明列表,为每个声明填充一个名称字符串,其值字符串以及可选Issuer
和ClaimValueType
字段。的ClaimType
类是其暴露了一些常见类型的权利要求的一个帮手。例如http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name
,每一个都是一个url ,但是您不必使用url,如最后添加的声明所示。
建立索赔之后,您可以创建一个new ClaimsIdentity
,传入您的索赔列表,并指定AuthenticationType
(以确保您的身份具有IsAuthenticated=true
)。最后,你可以创建一个新的ClaimsPrincipal
使用您的身份和登录用户。在这种情况下,我们说的是AuthenticationManager
使用"Cookie"
认证处理,这是我们必须配置为我们的中间件管道的一部分。
摘要
在这篇文章中,我描述了基于声明的身份验证如何工作以及如何将其应用于ASP.NET Core。在下一篇文章中,我将介绍身份验证过程的下一阶段-cookie中间件实际上是如何使用提供的主体登录您的。随后的文章将介绍如何使用多个身份验证处理程序,授权的工作方式以及ASP.NET Core Identity如何将它们全部绑定在一起。
网友评论