SecurityTokenInvalidAudienceException:IDX10214:受众验证失败

SecurityTokenInvalidAudienceException: IDX10214: Audience validation failed

我正在使用 Identity 和 Sustainsys.Saml2(用于 SAML 身份验证)开发 ASP.NET Core 2 应用程序。我已经在 Startup.cs 文件中进行了必要的配置。现在,当我 运行 项目并尝试使用 SAML2 登录(作为外部登录)时,输入凭据后出现以下错误:

SecurityTokenInvalidAudienceException: IDX10214: Audience validation failed. Audiences: '[PII is hidden]'. Did not match: validationParameters.ValidAudience: '[PII is hidden]' or validationParameters.ValidAudiences: '[PII is hidden]'. Microsoft.IdentityModel.Tokens.Validators.ValidateAudience(IEnumerable audiences, SecurityToken securityToken, TokenValidationParameters validationParameters) Microsoft.IdentityModel.Tokens.Saml2.Saml2SecurityTokenHandler.ValidateConditions(Saml2SecurityToken samlToken, TokenValidationParameters validationParameters) Microsoft.IdentityModel.Tokens.Saml2.Saml2SecurityTokenHandler.ValidateToken(string token, TokenValidationParameters validationParameters, out SecurityToken validatedToken) Sustainsys.Saml2.Saml2P.Saml2Response+d__60.MoveNext() System.Collections.Generic.List..ctor(IEnumerable collection) System.Linq.Enumerable.ToList(IEnumerable source) Sustainsys.Saml2.Saml2P.Saml2Response.GetClaims(IOptions options, IDictionary relayData) Sustainsys.Saml2.WebSso.AcsCommand.ProcessResponse(IOptions options, Saml2Response samlResponse, StoredRequestState storedRequestState) Sustainsys.Saml2.WebSso.AcsCommand.Run(HttpRequestData request, IOptions options) Sustainsys.Saml2.AspNetCore2.Saml2Handler+d__12.MoveNext() System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) Microsoft.AspNetCore.Authentication.AuthenticationMiddleware+d__6.MoveNext() System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.MigrationsEndPointMiddleware+d__4.MoveNext() System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.DatabaseErrorPageMiddleware+d__6.MoveNext() System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.DatabaseErrorPageMiddleware+d__6.MoveNext() System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware+d__7.MoveNext()

我不明白这是什么意思。我错过了什么吗?

这是我在启动文件中的内容

services.AddAuthentication()
        .AddSaml2(options => 
        {
            var spOptions = new SPOptions
            {
                EntityId = new EntityId("https://localhost:44373/Saml2"),
                ReturnUrl = new Uri("https://localhost:44373"),
                MinIncomingSigningAlgorithm = "http://www.w3.org/2000/09/xmldsig#rsa-sha1",                  
            };                

            options.SPOptions = spOptions;
            options.IdentityProviders.Add(new IdentityProvider(new EntityId("https://www.example.com/SSO/SAML/App"), options.SPOptions)
            {
                AllowUnsolicitedAuthnResponse = false,                  
                MetadataLocation = "https://www.example.com/SSO/SAMLMetadata/App",                  
                LoadMetadata = true,                  
            }); 
        });

提前致谢...

据我所知,此错误清楚地表明您的 SAML 令牌中的受众与您的启动配置中的值不同。比较这些值可能会有所帮助。有时会因为区分大小写而验证失败,所以你应该注意这种情况下你的受众是在token和configuration中。

根据源码(Saml2Response) and as Anders Abel指出,ValidAudience 属性是从你这里配置的SPOptions.EntityId初始化的:

var spOptions = new SPOptions
{
    EntityId = new EntityId("https://localhost:44373/Saml2"),
    ReturnUrl = new Uri("https://localhost:44373"),
    MinIncomingSigningAlgorithm = "http://www.w3.org/2000/09/xmldsig#rsa-sha1",                  
};

所以您应该将您配置的 EntityId 值与您的 saml-token 中的值进行比较,它可能如下所示:

<saml:Audience>The value here should be the same as in your startup configuration</saml:Audience>

IDX10214:如果您使用的是 Microsoft.Identity.Web 版本 1.4.1 或类似版本并且出现此异常(字面上复制,并且您必须更改日志级别),请检查 this 部分在 appsettings.json 中看到这个):

info: Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerHandler[1]
      Failed to validate the token.
      Microsoft.IdentityModel.Tokens.SecurityTokenInvalidAudienceException: IDX10214: Audience validation failed. Audiences: 'System.String'. Did not match: validationParameters.ValidAudience: 'System.String' or validationParameters.ValidAudiences: 'System.String'.
         at Microsoft.IdentityModel.Tokens.Validators.ValidateAudience(IEnumerable`1 audiences, SecurityToken securityToken, TokenValidationParameters validationParameters)
         at Microsoft.Identity.Web.Resource.RegisterValidAudience.ValidateAudience(IEnumerable`1 audiences, SecurityToken securityToken, TokenValidationParameters validationParameters)
         at Microsoft.IdentityModel.Tokens.Validators.ValidateAudience(IEnumerable`1 audiences, SecurityToken securityToken, TokenValidationParameters validationParameters)
         at System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler.ValidateAudience(IEnumerable`1 audiences, JwtSecurityToken jwtToken, TokenValidationParameters validationParameters)
         at System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler.ValidateTokenPayload(JwtSecurityToken jwtToken, TokenValidationParameters validationParameters)
         at System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler.ValidateToken(String token, TokenValidationParameters validationParameters, SecurityToken& validatedToken)
         at Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerHandler.HandleAuthenticateAsync()

真正看到那些 System.String 的价值会更有帮助。如果使用 GUID 或可记忆和可读的东西并不重要。

本质上你必须将 Audience 属性 添加到 appsettings.json 并且 that 必须等于 Application ID URI in Azure 门户。我没有运气破解 ClientId 并匹配 Application ID URI。这不是最终解决方案 - AFAIK 必须仍然等于 Azure 门户中的 Application (client) ID,即没有任何前缀或后缀的 GUID。

在我的例子中,省略了 JwtSecurityToken 的发行者和受众。在我派生的 class UserService: IUserService 中,我在函数 generateJwtToken 中定义了 issuer 和 audience 变量。对于 ValidIssuer 和 ValidAudience,它们的变量必须匹配 startup.csv 中的 services.AddJwtBearer 变量。见(https://dotnetcoretutorials.com/2020/01/15/creating-and-validating-jwt-tokens-in-asp-net-core/).

引用:

The Issuer and Audience are funny things because realistically, you probably won’t have a lot of use for them. Issuer is “who” created this token, for example your website, and Audience is “who” the token is supposed to be read by. So a good example might be that when a user logs in, your authentication api (auth.mywebsite.com) would be the issuer, but your general purposes API is the expected audience (api.mywebsite.com). These are actually free text fields so they don’t have to be anything in particular, but later on when we validate the issuer/audience, we will need to know what they are.

public class 用户服务:IUserService {

private string generateJwtToken(long userId)
            {
                var secretKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_appSettings.Secret));
                var signinCredentials = new SigningCredentials(secretKey, SecurityAlgorithms.HmacSha256);
                var tokenOptions = new JwtSecurityToken(
                    issuer: "http://localhost:5000",
                    audience: "http://localhost:5000",
                    claims: new List<Claim> {
                        new Claim(ClaimTypes.Name, userId.ToString()),
                        new Claim(ClaimTypes.Role, "Operator")
                    },
                    expires: DateTime.UtcNow.AddDays(7),
                    signingCredentials: signinCredentials
                );
                var tokenString = new JwtSecurityTokenHandler().WriteToken(tokenOptions);
                return tokenString;

            }

}

startup.cs

public void ConfigureServices(IServiceCollection services)
        {

services.AddAuthentication(opt =>
            {
                opt.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
                opt.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
            })
            .AddJwtBearer(opt =>
            {
                opt.RequireHttpsMetadata = false;
                opt.SaveToken = true;
                opt.TokenValidationParameters = new TokenValidationParameters
                {
                    ValidateIssuerSigningKey = true,
                    ValidateIssuer = true,
                    ValidateAudience = true,
                    ValidateLifetime = true,
                    IssuerSigningKey = new SymmetricSecurityKey(key),
                    ValidIssuer = "http://localhost:5000",
                    ValidAudience = "http://localhost:5000"
                };
            });

}