在ASP.NET Core中创建和使用 JWT 令牌

432 阅读5分钟

我最近一直在使用JWT令牌作为API的首选身份验证方法。并且,我处理了很多有关JWT令牌身份验证和授权方面的事情,有必要记录一下。

1、有关授权

首先,有很多关于使用ASP.NET Identity处理身份验证/授权的文档,因此,当你按照之前mvc既定授权方案使用,利用UserManager 系列的表和类库,可以享受类库中自带属性,例如[Authorize] 等。

但是,我总会碰到自定义,而现成的组件则无法提供这些灵活性。如果你正有此方面的困扰,希望本文能帮你解决一些问题!

2、在ASP.NET Core中创建JWT令牌

首先让我们看一下如何手动创建JWT令牌。对于我们的示例,我们将简单地创建一个将令牌作为字符串返回的服务。

然后,您将返回由您自己决定的令牌(标头,响应正文等)。

我还将在以下示例中指出,我们有类似硬编码的“秘钥”之类的东西。我这样做是出于演示的目的,但是很显然,您将希望可以按照配置驱动。那请您以此为起点,然后将其修改为可用于生产的代码。

生成JWT令牌的代码如下所示:

public string GenerateToken(int userId)
{
    //密钥密钥
	var mySecret = "asdv234234^&%&^%&^hjsdfb2%%%";
	var mySecurityKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(mySecret));

	var myIssuer = "http://codeex.cn";
	var myAudience = "http://webmote.net";

	var tokenHandler = new JwtSecurityTokenHandler();
	var tokenDescriptor = new SecurityTokenDescriptor
	{
		Subject = new ClaimsIdentity(new Claim[]
		{
			new Claim(ClaimTypes.NameIdentifier, userId.ToString()),
		}),
		Expires = DateTime.UtcNow.AddDays(7),
		Issuer = myIssuer,
		Audience = myAudience,
		SigningCredentials = new SigningCredentials(mySecurityKey, SecurityAlgorithms.HmacSha256Signature)
	};

	var token = tokenHandler.CreateToken(tokenDescriptor);
	return tokenHandler.WriteToken(token);
}

让我们逐步介绍一下。

这里有一个安全密钥,该密钥本质上用于对令牌进行“签名”。当我们在另一端收到令牌时,可以验证此签名以确保它是由我们创建的。

令牌本身实际上是可读的,即使您对其进行签名,因此也切勿在其中放入敏感信息。签名仅验证创建令牌的人是我们,以及令牌是否被篡改,但不会“加密”令牌。

Issuer和Audience是有趣的事情,因为实际上,可能没有太多用处。
发行者是“谁”创建了此令牌,例如您的网站,而受众是“谁”使用了该令牌。因此,一个很好的例子可能是,当用户登录时,您的身份验证api(auth.codeex.com)将是颁发者,而您的通用API是预期的受众(api.codeex.com)。

这些实际上是自由文本字段,因此不需要特别说明,但是稍后在验证发行者/受众时,我们将需要知道它们是什么。

我们将创建一个7天的令牌,但您可以将其设置为所需的任何值(或使其完全不过期),其余代码仅是.NET Core特定的令牌编写代码。

3、身份声明

声明实际上是一个简单的概念,但是围绕它们进行“抽象”思考的文章非常多。

简单来说,声明是存储在令牌中关于持有该令牌的用户/个人的“事实”。

例如,如果我以管理员角色登录自己的网站,则我的令牌可能会“声称”我的角色是管理员。

或用一句话写成“持有此令牌的人可以声称他们是管理员”。

这就像您可以在cookie中存储任意信息一样,您基本上可以在JWT令牌中执行相同的操作。

例如,我们可以执行以下操作:

Subject = new ClaimsIdentity(new Claim[]
{
	new Claim("UserRole", "Administrator"),
})

注意,我们不像在第一个示例中那样使用“ ClaimTypes”静态类,我们只是使用一个随便的字符串来定义声明名称,然后说出值是什么。

您基本上可以对所需的任意信息执行此操作,但请再次记住,任何人都可以解码JWT令牌,因此您不应在其中存储任何敏感的信息

还有一种很棒的方式,是将声明类型存储为静态const / readonly。例如 :

public static readonly string ClaimsRole = "UserRole";

[...]

Subject = new ClaimsIdentity(new Claim[]
{
	new Claim(ClaimsRole, "Administrator"),
})

您可能需要在多个位置使用ClaimType字符串,因此最好将它设置为静态变量。

4、验证令牌

一旦您创建了令牌,下一步就是在用户向您发送令牌时对其进行验证。

现在我个人喜欢将其发送到x-api-token之类的标头中,但是由于它只是一个字符串,因此您可以按照自己喜欢的任何方式发送它。来,看个例子吧。

public bool ValidateCurrentToken(string token)
{
	var mySecret = "asdv234234^&%&^%&^hjsdfb2%%%";
	var mySecurityKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(mySecret));

	var myIssuer = "http://codeex.cn";
	var myAudience = "http://webmote.net";

	var tokenHandler = new JwtSecurityTokenHandler();
	try
	{
		tokenHandler.ValidateToken(token, new TokenValidationParameters
		{
			ValidateIssuerSigningKey = true,
			ValidateIssuer = true,
			ValidateAudience = true,
			ValidIssuer = myIssuer,
			ValidAudience = myAudience,
			IssuerSigningKey = mySecurityKey
		}, out SecurityToken validatedToken);
	}
	catch
	{
		return false;
	}
	return true;
}

有没有注意到,我不得不将安全密钥,颁发者和访问者复制并粘贴到此方法中。与往常一样,在配置类中配置更好。

验证的原理是啥呢?

实际上非常简单。我们创建一个TokenHandler,它是用于处理JWT令牌的.NET Core内置类,我们将令牌以及“预期”发行者,受众和安全密钥传递给它,并进行验证。这可以验证发行人和受众是否符合我们的预期,并使用正确的密钥对令牌进行签名。如果令牌未通过验证,则会引发异常,因此我们可以简单地捕获此异常并返回false。

5、获取身份声明

难题的最后一部分是获取声明。假设我们已经验证了令牌本身,这实际上很容易。

public string GetClaim(string token, string claimType)
{
	var tokenHandler = new JwtSecurityTokenHandler();
	var securityToken = tokenHandler.ReadToken(token) as JwtSecurityToken;

	var stringClaimValue = securityToken.Claims.First(claim => claim.Type == claimType).Value;
	return stringClaimValue;
}

6、增加AddAuthentication / AddJwtBearer?

您可能已经阅读了使用以下代码的文档:

ervices.AddAuthentication(x =>
{
	x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
	x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(x =>
{
	x.TokenValidationParameters = new TokenValidationParameters();
});

或通过它的一些变体来设置带有签名密钥,受众和发行者的令牌验证参数。

仅当使用默认的Authorize 属性时,此方法才有效。这些设置都是配置ASP.NET Core内置授权处理程序的一种方式。如果您自己创建/验证JWT令牌,它不会设置任何其他全局设置。

7、小结

好像看起来也没啥,很简单的哦。