【问题标题】:Can't get asp .net core 2.2 to validate my JWT无法让 asp .net core 2.2 验证我的 JWT
【发布时间】:2019-10-14 13:31:28
【问题描述】:

(TL;DR:检查上次更新)

第一个问题是 API 在不同版本的 .net core 之间发生了变化。

第二个是我在网上找到的每个示例都略有不同,对于什么是做什么没有真正的权威答案;相反,我一直在寻找带有“哦,改变这个对我有用”的帖子,但看起来每个人都和我一样,在尝试它而不是完全理解。

总之,代码如下:

        Services.AddAuthentication(A =>
        {
            A.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
            A.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
        })
        .AddJwtBearer(O =>
        {
            O.RequireHttpsMetadata = false;
            O.SaveToken = true;
            O.TokenValidationParameters = new TokenValidationParameters
            {
                ValidateIssuerSigningKey = true,
                IssuerSigningKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(AuthJWTPublicKey)),
                ValidateIssuer = false,
                ValidateAudience = false
            };
        });

我有一个有效的 JWT,但对我的控制器的任何调用都会立即导致错误 401。

我尝试用以下方式装饰控制器:

[授权]

[授权(Roles = "offline_access")]

[授权(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme, Roles = "offline_access")]

但没有运气

JWT 内容为:

{  
   "jti":"837c2dd1-cca5-491e-84a4-b17429366df5",
   "exp":1558355152,
   "nbf":0,
   "iat":1558354852,
   "iss":"xxxx",
   "aud":"account",
   "sub":"4ed1c313-c692-44db-86d3-7605f3e2c2c1",
   "typ":"Bearer",
   "azp":"test-app",
   "auth_time":1558354850,
   "session_state":"e40c9a95-ae8a-4d6e-b2a4-ad5e833867ea",
   "acr":"1",
   "realm_access":{  
      "roles":[  
         "offline_access",
         "uma_authorization"
      ]
   },
   "resource_access":{  
      "account":{  
         "roles":[  
            "manage-account",
            "manage-account-links",
            "view-profile"
         ]
      }
   },
   "scope":"openid email profile",
   "email_verified":true,
   "name":"firstd6d05 last29954",
   "preferred_username":"xxxx",
   "given_name":"firstd6d05",
   "family_name":"last29954",
   "email":"xxxx",
   "group":[  
      "/Administrators"
   ]
}

我的目标纯粹是验证 JWT 的签名(最终它会过期)并有权访问控制器中的令牌。

我通过标头传递 JWT,在“授权”下并在令牌前面带有文本“承载”。

我错过了什么?


按照下面的答案,我做了以下更改:

我补充说:

Services.AddTransient<IClaimsTransformation, ClaimsTransformer>();

然后添加以下带中断的类

internal class ClaimsTransformer : IClaimsTransformation
{
    public Task<ClaimsPrincipal> TransformAsync(ClaimsPrincipal principal)
    {
        Debugger.Break();
    }
}

但我仍然收到带有 Keycloak 令牌的 401;所以看起来它在被拒绝之前没有到达索赔转换器。


我添加了 ASP 的调试输出。

它位于:https://pastebin.com/qvGsQG6j

相关部分似乎是:

    info: Microsoft.AspNetCore.Authorization.DefaultAuthorizationService[2]
          => ConnectionId:0HLN4CPASJL8F => RequestId:0HLN4CPASJL8F:00000001 RequestPath:/helpers/log => Test.WebControllers.HelpersController.GetLog (Test)
          Authorization failed.
    Microsoft.AspNetCore.Authorization.DefaultAuthorizationService:Info:Authorization failed.
    Microsoft.AspNetCore.Authorization.DefaultAuthorizationService:Info:Authorization failed.
    info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[3]
          => ConnectionId:0HLN4CPASJL8F => RequestId:0HLN4CPASJL8F:00000001 RequestPath:/helpers/log => Test.WebControllers.HelpersController.GetLog (Test)
          Authorization failed for the request at filter 'Microsoft.AspNetCore.Mvc.Authorization.AuthorizeFilter'.
    Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker:Info:Authorization failed for the request at filter 'Microsoft.AspNetCore.Mvc.Authorization.AuthorizeFilter'.
    Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker:Info:Authorization failed for the request at filter 'Microsoft.AspNetCore.Mvc.Authorization.AuthorizeFilter'.
    info: Microsoft.AspNetCore.Mvc.ChallengeResult[1]
          => ConnectionId:0HLN4CPASJL8F => RequestId:0HLN4CPASJL8F:00000001 RequestPath:/helpers/log => Test.WebControllers.HelpersController.GetLog (Test)

这一行可以解释这个问题:

过滤器“Microsoft.AspNetCore.Mvc.Authorization.AuthorizeFilter”处的请求授权失败

但“失败”对我来说似乎有点模糊。


最后一次测试:

我有这个令牌:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9uYW1lIjoibXkgbmFtZSIsImh0dHA6Ly9zY2hlbWFzLnhtbHNvYXAub3JnL3dzLzIwMDUvMDUvaWRlbnRpdHkvY2xhaW1zL2VtYWlsYWRkcmVzcyI6Im5vQGVtYWlsLmNvbSIsImh0dHA6Ly9zY2hlbWFzLm1pY3Jvc29mdC5jb20vd3MvMjAwOC8wNi9pZGVudGl0eS9jbGFpbXMvZXhwaXJhdGlvbiI6IjA1LzMxLzIwMTkgMDA6MTM6MjAiLCJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9zaWQiOiI3Y2FjZjdiNS04NjY5LTQ2OWUtOTg4NS1kNDgwNGYyOGNjNGEiLCJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA5LzA5L2lkZW50aXR5L2NsYWltcy9hY3RvciI6ImQyZjZiNzI0LWE0ZjktNGJkZC1hNjQ3LTRkM2UxYTU5NjhkZSIsIm5iZiI6MTU1OTE3NTIwMCwiZXhwIjoxNTU5MjYxNjAwLCJpc3MiOiJ0ZXN0IiwiYXVkIjoidGVzdC1hcHAifQ.y42RUvMM69aDTvCydoU3mOKu2giub6OvKpd-RNVmom4

用钥匙:

MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAjayVuqZOuKK38rhsRwrRGzVcv/7b4fHXrzpol3K5TTAPttNUaQvCKQD7BQN+V8nvkBsQcxPk5ONnxzbFb/npENC4UtwK5J6iiVrinE7sDrWZQNo9LkwbuG9x0fuuf8U3H2CnwZEfFaf2kbU1v7XosNGi+aYASupvhwoiJtK+17ZPloxSQy3Qny2tQWi7Dh/Cr5+m5JBy6HeGLq2cq+oalFYzrGGmQXndLtJpBZgrPd7nR6lJSMiRcJtcpxTJbYTEVVXoB0SR1bPmYYB/6y7klVDVedTow+1mwZrDMrbRiTBPSifCIBs3rxLQaL207blg+kj+EVLED5fZSOBlOyTOYQIDAQAB

它解码为(由 jwt.io 测试):

{
  "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name": "my name",
  "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress": "no@email.com",
  "http://schemas.microsoft.com/ws/2008/06/identity/claims/expiration": "05/31/2019 00:13:20",
  "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/sid": "7cacf7b5-8669-469e-9885-d4804f28cc4a",
  "http://schemas.xmlsoap.org/ws/2009/09/identity/claims/actor": "d2f6b724-a4f9-4bdd-a647-4d3e1a5968de",
  "nbf": 1559175200,
  "exp": 1559261600,
  "iss": "test",
  "aud": "test-app"
}

由以下代码生成:

        var Expiration = DateTime.UtcNow + TimeSpan.FromDays(1);
        var Identity = new ClaimsIdentity(new[]
        {
            new Claim(ClaimTypes.Name,          "my name"),
            new Claim(ClaimTypes.Email,         "no@email.com"),
            new Claim(ClaimTypes.Expiration,    Expiration.ToString(CultureInfo.InvariantCulture)),
            new Claim(ClaimTypes.Sid,           Guid.NewGuid().ToString()),
            new Claim(ClaimTypes.Actor,         Guid.NewGuid().ToString()),
        });

        var Token = new JwtSecurityToken
        (
            issuer:             "test",
            audience:           "test-app",
            claims:             Identity.Claims,
            notBefore:          DateTime.UtcNow,
            expires:            Expiration,
            signingCredentials: new SigningCredentials(new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_JwtRsaPublicKey)), SecurityAlgorithms.HmacSha256)
        );

        var TokenString = new JwtSecurityTokenHandler().WriteToken(Token);
        return TokenString;

在asp中,我有以下代码:

                    Services.AddAuthentication(A =>
                    {
                        A.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
                        A.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
                    })
                    .AddJwtBearer(O =>
                    {
                        O.RequireHttpsMetadata = false;
                        O.SaveToken = true;
                        O.IncludeErrorDetails = true;
                        O.TokenValidationParameters = new TokenValidationParameters
                        {
                            ValidateIssuerSigningKey = true,
                            IssuerSigningKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(AuthJWTPublicKey)),
                            ValidateIssuer = false,
                            ValidateAudience = false
                        };
                    });

我有一个控制器方法:

  [Authorize]

但我不断收到 401,无论我尝试什么,我都会收到 401...

即使我将 ValidateIssuerSigningKey 设置为 false。我得到一个 401

设置是:

在 ConfigureServices 中,我有:

Services.AddMvc()
and the code above

在配置中,我有:

Application.UseAuthentication();
Application.UseMvc(Routes => { Routes.MapRoute("default_route", "{controller}/{action}/{id?}"); });

【问题讨论】:

  • 你用过JwtTokenBuilder吗,能不能把你创建jwt数据的部分加进去。
  • 令牌是由keycloak创建的
  • 有一篇关于 net core 中 jwt 使用的好帖子,它使用了 JwtTokenBuilder => medium.com/@engr.mmohsin/…
  • 我已经用调试输出编辑了帖子

标签: asp.net-core jwt


【解决方案1】:

根据dotnet/corefx

公共静态类 ClaimTypes { 内部常量字符串 ClaimTypeNamespace ="http://schemas.microsoft.com/ws/2008/06/identity/claims"; …… public const string Role = ClaimTypeNamespace + "/role"; ...

常量 ClaimTypes.Rolehttp://schemas.microsoft.com/ws/2008/06/identity/claims/role

注意你的控制器需要offline_access的角色:

[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme, Roles = "offline_access")]

换句话说,它期望解码后的JWT具有http://schemas.microsoft.com/ws/2008/06/identity/claims/role的属性来配置角色:

{ "nbf": ..., “经验”:..., “是”:……, “奥迪”:..., ..., “http://schemas.microsoft.com/ws/2008/06/identity/claims/role”:[ "offline_access", “……其他角色” ], }

但是,您可以通过以下方式配置角色:

   "realm_access":{  
      "roles":[  
         "offline_access",
         "uma_authorization"
      ]
   },

最简单的方法是重建令牌以配置offline_access的角色:

    ClaimsIdentity identity = new ClaimsIdentity(new[] {
        new Claim(ClaimTypes.Name,"1024"),
        new Claim(ClaimTypes.Role,"offline_access"),
        new Claim(ClaimTypes.Role,"...other roles"),
        // ... other claims
    });

    var token = new JwtSecurityToken
    (
        issuer: _issuer,
        audience: _audience,
        claims: identity.Claims,
        expires: expiry,
        notBefore: DateTime.UtcNow,
        signingCredentials: new SigningCredentials(new SymmetricSecurityKey(Encoding.UTF8.GetBytes(mykeyname)),
                SecurityAlgorithms.HmacSha256)
    );

    var tokenStr = tokenHandler.WriteToken(token);

或者您可以通过注册自定义 IClaimsTransformation 将 keycloak 的样式声明转换为 Microsoft 的样式,请参阅 ASP.NET Core 团队 https://github.com/aspnet/AspNetCore/blob/658b37d2bd3b0da2c07e25b53fa5c97ce8d656d3/src/Security/samples/ClaimsTransformation/Startup.cs#L34 的示例

【讨论】:

  • 由于我将处理几个角色,因此实现声明转换是否有意义,以便管道在每次调用时将 keycloak jwt 解析为与 Microsoft 兼容的令牌?
  • @Thomas 是的。这就是IClaimsTransformation 服务的设计目的。 Every time the authentication succeeds,将调用此服务。
  • 我已尝试添加声明转换器(我已编辑问题),但在到达转换器之前仍会立即被拒绝。
  • @Thomas 对于您的最后两次编辑:“但我一直收到 401,无论我尝试什么,我都会收到 401...:”。也许我在问一个愚蠢的问题:您是否通过app.UseAuthentication();启用身份验证中间件
  • 是的,它就在那里;它在 UseMVC 之前,但我也尝试将它放在之后。
【解决方案2】:

我不确定变量中存储了什么,但看起来它不是私有令牌:

IssuerSigningKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(AuthJWTPublicKey)),

据我所知,这是一个私有对称密钥,也许您在其中传递了错误的值?

【讨论】:

  • 是解码token的关键;密钥肯定是有效的,但它的传递方式可能不是
  • 我刚刚验证了密钥,没有问题,它与Keycloak中的匹配
  • 是的,我做到了,它验证了;但是我发现了另一个潜在的问题:bitbucket.org/b_c/jose4j/issues/134/…我不知道它是否会影响 ASP
猜你喜欢
  • 2019-09-01
  • 2019-12-24
  • 2023-04-01
  • 1970-01-01
  • 2019-09-06
  • 2021-10-29
  • 2017-09-11
  • 2018-11-08
  • 2020-07-23
相关资源
最近更新 更多