如何在安全 REST 端点已存在时验证 SOAP 服务中的令牌
How to Validate a token in a SOAP service when a secured REST endpoint already exists
我有一些 Frankenstien 服务,因为它具有由相同代码库托管在相同 URL 上的 SOAP 和 REST 端点。我正在使用客户端凭据授予流程来成功保护 REST 端点,但我想使用相同的过程来保护 SOAP 调用。 startup.cs 像这样初始化身份服务器承载令牌身份验证:
JwtSecurityTokenHandler.InboundClaimTypeMap = new Dictionary<string, string>();
app.UseIdentityServerBearerTokenAuthentication(new IdentityServerBearerTokenAuthenticationOptions
{
Authority = ConfigurationManager.AppSettings["IdentityServerUrl"],
RequiredScopes = new[] { ConfigurationManager.AppSettings["IdentityServerScopes"] }
});
对于 REST 端点,我添加了一个
[Authorize]
代码修饰,一切正常。对于 SOAP 端,我重新调整了密码字段的用途并通过它发送了令牌,并且可以像这样对其进行解码:
string sPassword = request.Authentication.Password;
if (sPassword.Contains("."))
{
"\nAccess Token (decoded):".ConsoleGreen();
var parts = sPassword.Split('.');
var header = parts[0];
var claims = parts[1];
Console.WriteLine(JObject.Parse(Encoding.UTF8.GetString(Base64Url.Decode(header))));
Console.WriteLine(JObject.Parse(Encoding.UTF8.GetString(Base64Url.Decode(claims))));
}
我可以看到声明,但这并没有验证令牌。从这里我拼凑了一个 ValidateToken 方法,该方法抛出有关签名验证失败的异常。无法解析 SecurityKeyIdentifier。我相当确定所有内容都已由 IdentityServer3 证书签名,但我一直在尝试创建证书。我的 KeyStore 中没有任何证书,并且想要一个不需要我将证书插入 KeyStore 的解决方案。这是尝试:
public static bool VerifyToken(string token)
{
const string thumbPrint = "6bf8e136eb36d4a56ea05c7ae4b9a45b63bf975d"; // correct thumbprint of certificate
var cert = X509CertificateHelper.FindByThumbprint(StoreName.My, StoreLocation.LocalMachine, thumbPrint).First();
var validationParameters = new TokenValidationParameters()
{
//IssuerSigningToken = new BinarySecretSecurityToken(_key),
IssuerSigningToken = new X509SecurityToken(cert),
ValidAudience = "https://securityeli.twcable.com/core/resources",
ValidIssuer = "https://securityeli.twcable.com/core",
ValidateLifetime = true,
ValidateAudience = true,
ValidateIssuer = true
//ValidateIssuerSigningKey = true
};
var tokenHandler = new JwtSecurityTokenHandler();
SecurityToken validatedToken = null;
try
{
tokenHandler.ValidateToken(token, validationParameters, out validatedToken);
}
catch (Exception ex)
{
throw new Exception(ex.Message);
}
//... manual validations return false if anything untoward is discovered
return validatedToken != null;
}
public class X509CertificateHelper
{
public static IEnumerable<X509Certificate2> FindByThumbprint(StoreName storeName, StoreLocation storeLocation, string thumbprint)
{
var store = new X509Store(storeName, storeLocation);
store.Open(OpenFlags.ReadOnly);
var certificates = store.Certificates.Find(X509FindType.FindByThumbprint, thumbprint, false);
foreach (var certificate in certificates)
{
yield return certificate;
}
store.Close();
}
}
当前进程不起作用,因为我的密钥库中没有密钥。 BinarySecretSecurityToken 失败是因为我不知道密钥长度?
我还要回到房子的 REST 端,它使用 Authorize 标签验证不记名令牌,所以我应该可以访问证书,但不知道如何将其从应用。我可以在 Startup 中看到它通过了我无法访问的 IAPPBuilder 应用程序。
两个问题是如何创建证书以验证在 C# 中的 IdentityServer3 中创建的令牌?我能以某种方式检索该证书吗?
在尝试了多条路径后,我终于找到了可行的方法,我将尝试捕获相关部分,以防其他人尝试做同样的事情。
首先我将传入的令牌分成几个部分:
var parts = sPassword.Split('.');
var header = parts[0];
var claims = parts[1];
var token = new JwtSecurityToken(sPassword);
然后我设置了一些变量并调用了自定义 VerifyToken 方法:
CustomResponse customResponse = null;
SecurityToken validatedToken = null;
ClaimsPrincipal claimsPrincipal = null;
if (VerifyToken(sPassword, ref customResponse, ref validatedToken, ref claimsPrincipal))
{
// Process SOAP request after authentication
}
else
return customResponse; // token wasn't authenticated, and not authorized message was set in the VerifyToken method
VerifyToken 方法如下所示:
public static bool VerifyToken(string token, ref CustomResponse customResponse, ref SecurityToken validatedToken, ref ClaimsPrincipal claimsPrincipal)
{
// This was the biggest challenge in finding the cert that is used to validate the token
var certString = "Found in the CallbackController.cs in the IdentityServer3.Samples repository"
var cert = new X509Certificate2(Convert.FromBase64String(certString));
// Setting what you'd like the authorization to validate.
var validationParameters = new TokenValidationParameters()
{
IssuerSigningToken = new X509SecurityToken(cert),
ValidAudience = ConfigurationManager.AppSettings["IdentityServerUrl"] + "/resources",
ValidIssuer = ConfigurationManager.AppSettings["IdentityServerUrl"],
ValidateLifetime = true,
ValidateAudience = true,
ValidateIssuer = true,
ValidateIssuerSigningKey = true
};
var tokenHandler = new JwtSecurityTokenHandler();
try
{
claimsPrincipal = tokenHandler.ValidateToken(token, validationParameters, out validatedToken);
}
catch (SecurityTokenValidationException e)
{
//HttpContext.Current.Response.StatusCode = 401;
//statusCode = HttpStatusCode.Unauthorized;
customResponse = new CustomResponse();
customResponse.ServiceReturnStatus = new ServiceReturnStatus();
customResponse.ServiceReturnStatus.ReturnCode = -401;
customResponse.ServiceReturnStatus.ReturnMessage = "Unauthorized";
}
catch (Exception e)
{
//HttpContext.Current.Response.StatusCode = 403;
//statusCode = HttpStatusCode.InternalServerError;
customResponse = new CustomResponse();
customResponse.ServiceReturnStatus = new ServiceReturnStatus();
customResponse.ServiceReturnStatus.ReturnCode = -403;
customResponse.ServiceReturnStatus.ReturnMessage = "Internal Server Error";
}
//... manual validations return false if anything untoward is discovered
return validatedToken != null;
}
private string GetClaimFromPrincipal(ClaimsPrincipal principal, string claimType)
{
var uidClaim = principal != null && principal.Claims != null ? principal.Claims.FirstOrDefault(s => s.Type == claimType) : null;
return uidClaim != null ? uidClaim.Value : null;
}
我还添加了一个 GetClaimFromPrincipal,您可以使用它从委托人那里获得索赔。
就是这样,它看起来并不那么复杂,但我确实花了很多时间来尝试和错误才能让它发挥作用。我仍然喜欢使用 Owin 启动信息来 validate/authorize 令牌的选项,因为我所做的基本上就是加载我在 Startup.cs 中加载的所有信息,如下所示:
app.UseIdentityServerBearerTokenAuthentication(new IdentityServerBearerTokenAuthenticationOptions
{
Authority = ConfigurationManager.AppSettings["IdentityServerUrl"],
RequiredScopes = new[] { ConfigurationManager.AppSettings["IdentityServerScopes"] }
});
我有一些 Frankenstien 服务,因为它具有由相同代码库托管在相同 URL 上的 SOAP 和 REST 端点。我正在使用客户端凭据授予流程来成功保护 REST 端点,但我想使用相同的过程来保护 SOAP 调用。 startup.cs 像这样初始化身份服务器承载令牌身份验证:
JwtSecurityTokenHandler.InboundClaimTypeMap = new Dictionary<string, string>();
app.UseIdentityServerBearerTokenAuthentication(new IdentityServerBearerTokenAuthenticationOptions
{
Authority = ConfigurationManager.AppSettings["IdentityServerUrl"],
RequiredScopes = new[] { ConfigurationManager.AppSettings["IdentityServerScopes"] }
});
对于 REST 端点,我添加了一个
[Authorize]
代码修饰,一切正常。对于 SOAP 端,我重新调整了密码字段的用途并通过它发送了令牌,并且可以像这样对其进行解码:
string sPassword = request.Authentication.Password;
if (sPassword.Contains("."))
{
"\nAccess Token (decoded):".ConsoleGreen();
var parts = sPassword.Split('.');
var header = parts[0];
var claims = parts[1];
Console.WriteLine(JObject.Parse(Encoding.UTF8.GetString(Base64Url.Decode(header))));
Console.WriteLine(JObject.Parse(Encoding.UTF8.GetString(Base64Url.Decode(claims))));
}
我可以看到声明,但这并没有验证令牌。从这里我拼凑了一个 ValidateToken 方法,该方法抛出有关签名验证失败的异常。无法解析 SecurityKeyIdentifier。我相当确定所有内容都已由 IdentityServer3 证书签名,但我一直在尝试创建证书。我的 KeyStore 中没有任何证书,并且想要一个不需要我将证书插入 KeyStore 的解决方案。这是尝试:
public static bool VerifyToken(string token)
{
const string thumbPrint = "6bf8e136eb36d4a56ea05c7ae4b9a45b63bf975d"; // correct thumbprint of certificate
var cert = X509CertificateHelper.FindByThumbprint(StoreName.My, StoreLocation.LocalMachine, thumbPrint).First();
var validationParameters = new TokenValidationParameters()
{
//IssuerSigningToken = new BinarySecretSecurityToken(_key),
IssuerSigningToken = new X509SecurityToken(cert),
ValidAudience = "https://securityeli.twcable.com/core/resources",
ValidIssuer = "https://securityeli.twcable.com/core",
ValidateLifetime = true,
ValidateAudience = true,
ValidateIssuer = true
//ValidateIssuerSigningKey = true
};
var tokenHandler = new JwtSecurityTokenHandler();
SecurityToken validatedToken = null;
try
{
tokenHandler.ValidateToken(token, validationParameters, out validatedToken);
}
catch (Exception ex)
{
throw new Exception(ex.Message);
}
//... manual validations return false if anything untoward is discovered
return validatedToken != null;
}
public class X509CertificateHelper
{
public static IEnumerable<X509Certificate2> FindByThumbprint(StoreName storeName, StoreLocation storeLocation, string thumbprint)
{
var store = new X509Store(storeName, storeLocation);
store.Open(OpenFlags.ReadOnly);
var certificates = store.Certificates.Find(X509FindType.FindByThumbprint, thumbprint, false);
foreach (var certificate in certificates)
{
yield return certificate;
}
store.Close();
}
}
当前进程不起作用,因为我的密钥库中没有密钥。 BinarySecretSecurityToken 失败是因为我不知道密钥长度?
我还要回到房子的 REST 端,它使用 Authorize 标签验证不记名令牌,所以我应该可以访问证书,但不知道如何将其从应用。我可以在 Startup 中看到它通过了我无法访问的 IAPPBuilder 应用程序。
两个问题是如何创建证书以验证在 C# 中的 IdentityServer3 中创建的令牌?我能以某种方式检索该证书吗?
在尝试了多条路径后,我终于找到了可行的方法,我将尝试捕获相关部分,以防其他人尝试做同样的事情。
首先我将传入的令牌分成几个部分:
var parts = sPassword.Split('.');
var header = parts[0];
var claims = parts[1];
var token = new JwtSecurityToken(sPassword);
然后我设置了一些变量并调用了自定义 VerifyToken 方法:
CustomResponse customResponse = null;
SecurityToken validatedToken = null;
ClaimsPrincipal claimsPrincipal = null;
if (VerifyToken(sPassword, ref customResponse, ref validatedToken, ref claimsPrincipal))
{
// Process SOAP request after authentication
}
else
return customResponse; // token wasn't authenticated, and not authorized message was set in the VerifyToken method
VerifyToken 方法如下所示:
public static bool VerifyToken(string token, ref CustomResponse customResponse, ref SecurityToken validatedToken, ref ClaimsPrincipal claimsPrincipal)
{
// This was the biggest challenge in finding the cert that is used to validate the token
var certString = "Found in the CallbackController.cs in the IdentityServer3.Samples repository"
var cert = new X509Certificate2(Convert.FromBase64String(certString));
// Setting what you'd like the authorization to validate.
var validationParameters = new TokenValidationParameters()
{
IssuerSigningToken = new X509SecurityToken(cert),
ValidAudience = ConfigurationManager.AppSettings["IdentityServerUrl"] + "/resources",
ValidIssuer = ConfigurationManager.AppSettings["IdentityServerUrl"],
ValidateLifetime = true,
ValidateAudience = true,
ValidateIssuer = true,
ValidateIssuerSigningKey = true
};
var tokenHandler = new JwtSecurityTokenHandler();
try
{
claimsPrincipal = tokenHandler.ValidateToken(token, validationParameters, out validatedToken);
}
catch (SecurityTokenValidationException e)
{
//HttpContext.Current.Response.StatusCode = 401;
//statusCode = HttpStatusCode.Unauthorized;
customResponse = new CustomResponse();
customResponse.ServiceReturnStatus = new ServiceReturnStatus();
customResponse.ServiceReturnStatus.ReturnCode = -401;
customResponse.ServiceReturnStatus.ReturnMessage = "Unauthorized";
}
catch (Exception e)
{
//HttpContext.Current.Response.StatusCode = 403;
//statusCode = HttpStatusCode.InternalServerError;
customResponse = new CustomResponse();
customResponse.ServiceReturnStatus = new ServiceReturnStatus();
customResponse.ServiceReturnStatus.ReturnCode = -403;
customResponse.ServiceReturnStatus.ReturnMessage = "Internal Server Error";
}
//... manual validations return false if anything untoward is discovered
return validatedToken != null;
}
private string GetClaimFromPrincipal(ClaimsPrincipal principal, string claimType)
{
var uidClaim = principal != null && principal.Claims != null ? principal.Claims.FirstOrDefault(s => s.Type == claimType) : null;
return uidClaim != null ? uidClaim.Value : null;
}
我还添加了一个 GetClaimFromPrincipal,您可以使用它从委托人那里获得索赔。
就是这样,它看起来并不那么复杂,但我确实花了很多时间来尝试和错误才能让它发挥作用。我仍然喜欢使用 Owin 启动信息来 validate/authorize 令牌的选项,因为我所做的基本上就是加载我在 Startup.cs 中加载的所有信息,如下所示:
app.UseIdentityServerBearerTokenAuthentication(new IdentityServerBearerTokenAuthenticationOptions
{
Authority = ConfigurationManager.AppSettings["IdentityServerUrl"],
RequiredScopes = new[] { ConfigurationManager.AppSettings["IdentityServerScopes"] }
});