나는 system.IdentityModel.Tokens.Jwt 라이브러리를 사용하여 알아 냈다. 버전 관리에 많은 어려움을 겪었으므로 사용하지 않은 nuget 패키지가 포함되었습니다. Microsoft.IdentityModel.Tokens.Jwt에 많은 문제가있어서 그 방법을 포기했습니다. 어쨌든 패키지는 다음과 같습니다.
<package id="Microsoft.IdentityModel.Protocol.Extensions" version="1.0.2.206221351" targetFramework="net462" />
<package id="Microsoft.Win32.Primitives" version="4.0.1" targetFramework="net462" />
<package id="System.IdentityModel.Tokens.Jwt" version="4.0.2.206221351" targetFramework="net462" />
<package id="System.Net.Http" version="4.1.0" targetFramework="net462" />
<package id="System.Security.Cryptography.Algorithms" version="4.2.0" targetFramework="net462" />
<package id="System.Security.Cryptography.Encoding" version="4.0.0" targetFramework="net462" />
<package id="System.Security.Cryptography.Primitives" version="4.0.0" targetFramework="net462" />
<package id="System.Security.Cryptography.X509Certificates" version="4.1.0" targetFramework="net462" />
그리고 여기에 코드가 있습니다. 작동 방식은 사용자 지정 키 확인자를 설정하는 것입니다. 이 키 리졸버는 토큰이 전달 될 때마다 호출됩니다. 아이 캐시 미스가 발생하면 토큰 서비스에 최신 키 세트를 다운로드하라는 새로운 요청을합니다. 처음에는 키의 여러 부분 (즉 만료되지 않았거나 유효한 발급자)을 확인하려고했지만 토큰이 올바르게 서명되었는지 확인할 수 없으면 이러한 체크를 추가하는 것이 무의미하기 때문에이를 결정했습니다. 공격자는 원하는대로 설정할 수 있습니다.
using Microsoft.IdentityModel.Protocols;
using System;
using System.Collections.Generic;
using System.IdentityModel.Tokens;
using System.Linq;
using System.Net.Http;
using System.Security.Cryptography;
using System.Threading;
using System.Threading.Tasks;
public class ValidationMiddleware
{
private readonly Func<IDictionary<string, object>, Task> next;
private readonly Func<string> tokenAccessor;
private readonly ConfigurationManager<OpenIdConnectConfiguration> configurationManager;
private readonly Object locker = new Object();
private Dictionary<string, SecurityKey> securityKeys = new Dictionary<string, SecurityKey>();
public ValidationMiddleware(Func<IDictionary<string, object>, Task> next, Func<string> tokenAccessor)
{
this.next = next;
this.tokenAccessor = tokenAccessor;
configurationManager = new ConfigurationManager<OpenIdConnectConfiguration>(
"url to open id connect token service",
new HttpClient(new WebRequestHandler()))
{
// Refresh the keys once an hour
AutomaticRefreshInterval = new TimeSpan(1, 0, 0)
};
}
public async Task Invoke(IDictionary<string, object> environment)
{
var token = tokenAccessor();
var validationParameters = new TokenValidationParameters
{
ValidAudience = "my valid audience",
ValidIssuer = "url to open id connect token service",
ValidateLifetime = true,
RequireSignedTokens = true,
RequireExpirationTime = true,
ValidateAudience = true,
ValidateIssuer = true,
IssuerSigningKeyResolver = MySigningKeyResolver, // Key resolver gets called for every token
};
JwtSecurityTokenHandler.InboundClaimTypeMap.Clear();
var tokenHandler = new JwtSecurityTokenHandler();
var claimsPrincipal = tokenHandler.ValidateToken(token, validationParameters, out SecurityToken validatedToken);
// Assign Claims Principal to the context.
await next.Invoke(environment);
}
private SecurityKey MySigningKeyResolver(string token, SecurityToken securityToken, SecurityKeyIdentifier keyIdentifier, TokenValidationParameters validationParameters)
{
var kid = keyIdentifier.OfType<NamedKeySecurityKeyIdentifierClause>().FirstOrDefault().Id;
if (!securityKeys.TryGetValue(kid, out SecurityKey securityKey))
{
lock (locker)
{
// Double lock check to ensure that only the first thread to hit the lock gets the latest keys.
if (!securityKeys.TryGetValue(kid, out securityKey))
{
// TODO - Add throttling around this so that an attacker can't force tonnes of page requests.
// Microsoft's Async Helper
var result = AsyncHelper.RunSync(async() => await configurationManager.GetConfigurationAsync());
var latestSecurityKeys = new Dictionary<string, SecurityKey>();
foreach (var key in result.JsonWebKeySet.Keys)
{
var rsa = RSA.Create();
rsa.ImportParameters(new RSAParameters
{
Exponent = Base64UrlEncoder.DecodeBytes(key.E),
Modulus = Base64UrlEncoder.DecodeBytes(key.N),
});
latestSecurityKeys.Add(key.Kid, new RsaSecurityKey(rsa));
if (kid == key.Kid)
{
securityKey = new RsaSecurityKey(rsa);
}
}
// Explicitly state that this assignment needs to be atomic.
Interlocked.Exchange(ref securityKeys, latestSecurityKeys);
}
}
}
return securityKey;
}
}
하여 키의 점점 주위에 일부 제한 토큰 서비스에 많은 왕복 강제 악의적 인 사용자를 중지하는 것이 만들 것입니다.