【问题标题】:How to Consume/Validate Token Issued by AspNet.Security.OpenIdConnect.Server (RC1)?如何使用/验证 AspNet.Security.OpenIdConnect.Server (RC1) 颁发的令牌?
【发布时间】:2016-03-31 15:43:18
【问题描述】:

我已经从有关如何实施 AspNet.Security.OpenIdConnect.Server 的帖子中了解了所有信息。

Pinpoint,你听到了吗? ;)

我已经设法将令牌发行和令牌消费分开。我不会显示“身份验证服务器端”,因为我认为这部分已经设置完毕,但我将展示如何在自定义 AuthorizationProvider 中构建身份验证票证:

public sealed class AuthorizationProvider : OpenIdConnectServerProvider
{
    // The other overrides are not show. I've relaxed them to always validate.

    public override async Task GrantResourceOwnerCredentials(GrantResourceOwnerCredentialsContext context)
    {
          // I'm using Microsoft.AspNet.Identity to validate user/password. 
          // So, let's say that I already have MyUser user from
          //UserManager<MyUser> UM:

            var identity = new ClaimsIdentity(OpenIdConnectServerDefaults.AuthenticationScheme);
            //identity.AddClaims(await UM.GetClaimsAsync(user));
            identity.AddClaim(ClaimTypes.Name, user.UserName);

            (await UM.GetRolesAsync(user)).ToList().ForEach(role => {
                identity.AddClaim(ClaimTypes.Role, role);
            });

            var ticket = new AuthenticationTicket(new ClaimsPrincipal(identity),
                                                  new AuthenticationProperties(),
                                                  context.Options.AuthenticationScheme);
            // Some new stuff, per my latest research
            ticket.SetResources(new[] { "my_resource_server" });
            ticket.SetAudiences(new[] { "my_resource_server" });
            ticket.SetScopes(new[] { "defaultscope" });

            context.Validated(ticket);
        }
    }

并在认证服务器上启动:

using System;
using Microsoft.AspNet.Builder;
using Microsoft.AspNet.Hosting;
using Microsoft.Data.Entity;
using Microsoft.Extensions.DependencyInjection;

using MyAuthServer.Providers;

namespace My.AuthServer
{
    public class Startup
    {
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddAuthentication();
            services.AddCaching();
            services.AddMvc();

            string connectionString = "there is actually one";

            services.AddEntityFramework()
                    .AddSqlServer()
                    .AddDbContext<MyDbContext>(options => {
                        options.UseSqlServer(connectionString).UseRowNumberForPaging();
                    });

            services.AddIdentity<User, Role>()
                    .AddEntityFrameworkStores<MyDbContext>().AddDefaultTokenProviders();
        }

        public void Configure(IApplicationBuilder app)
        {
            app.UseIISPlatformHandler();

            app.UseOpenIdConnectServer(options => {
                options.ApplicationCanDisplayErrors = true;
                options.AllowInsecureHttp = true;                
                options.Provider = new AuthorizationProvider();
                options.TokenEndpointPath = "/token";
                options.AccessTokenLifetime = new TimeSpan(1, 0, 0, 0);
                options.Issuer = new Uri("http://localhost:60556/");
            });

            app.UseMvc();
            app.UseWelcomePage();
        }

        public static void Main(string[] args) => WebApplication.Run<Startup>(args);
    }
}

果然,当我收到这个 HTTP 请求时,我确实得到了一个访问令牌,但我不确定该访问令牌是否包含资源服务器期望的所有数据。

POST /token HTTP/1.1
Host: localhost:60556
Content-Type: application/x-www-form-urlencoded

username=admin&password=pw&grant_type=password

现在,在资源服务器端,我正在使用 JWT 承载身份验证。在启动时,我有:

using Microsoft.AspNet.Builder;
using Microsoft.AspNet.Hosting;
using Microsoft.Data.Entity;
using Microsoft.Extensions.DependencyInjection;

namespace MyResourceServer
{
    public class Startup
    {            
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddMvc();

            string connectionString = "there is actually one";

            services.AddEntityFramework()
                    .AddSqlServer()
                    .AddDbContext<MyDbContext>(options => {
                        options.UseSqlServer(connectionString).UseRowNumberForPaging();                        
                    });

            services.AddIdentity<User, Role>()
                    .AddEntityFrameworkStores<MyDbContext>().AddDefaultTokenProviders();
        }

        public void Configure(IApplicationBuilder app)
        {
            app.UseIISPlatformHandler();
            app.UseMvc();
            app.UseWelcomePage();

            app.UseJwtBearerAuthentication(options => {
                options.Audience = "my_resource_server";  
                options.Authority = "http://localhost:60556/"; 
                options.AutomaticAuthenticate = true;
                options.RequireHttpsMetadata = false;                
            });
        }

        public static void Main(string[] args) => WebApplication.Run<Startup>(args);
    }
}

当我向资源服务器发出这个 HTTP 请求时,我得到一个 401 Unauthorized:

GET /api/user/myroles HTTP/1.1
Host: localhost:64539
Authorization: Bearer eyJhbGciOiJS...
Content-Type: application/json;charset=utf-8

拥有到/api/user/myroles 的路由的控制器用一个没有参数的普通[Authorize] 装饰。

我觉得我在身份验证和资源服务器中都缺少一些东西,但不知道它们是什么。

询问“如何验证 AspNet.Security.OpenIdConnect.Server 颁发的令牌”的其他问题没有答案。我将不胜感激。

另外,我注意到示例提供程序中注释掉了 OAuth Introspection,并且在某处读到 Jwt 不会很快得到支持。我找不到给我 OAuth Instrospection 的依赖项。


更新我已经包含了我的 startup.cs,来自每个身份验证和资源服务器。会不会有什么错误会导致资源服务器对每个请求总是返回 401?

在整个过程中我没有真正触及的一件事是签名。它似乎在身份验证服务器上为 JWT 生成签名,但资源服务器(我猜)不知道签名密钥。回到 OWIN 项目,我必须创建一个机器密钥并放在两台服务器上。

【问题讨论】:

  • 嘿。如果您看到未回答的 ASOS 问题,请将链接发送给我,以便我回答;)
  • 我的意思是这些问题的答案回答了他们的问题,而不是我的问题。

标签: authentication oauth-2.0 asp.net-core openid-connect aspnet-contrib


【解决方案1】:

编辑:您的中间件实例的顺序不正确:the JWT bearer middleware must be registered before MVC:

app.UseIISPlatformHandler();

app.UseJwtBearerAuthentication(options => {
    options.Audience = "my_resource_server";  
    options.Authority = "http://localhost:60556/"; 
    options.AutomaticAuthenticate = true;
    options.RequireHttpsMetadata = false;
});

app.UseMvc();
app.UseWelcomePage();

果然,当我收到这个 HTTP 请求时,我确实得到了一个访问令牌,但我不确定该访问令牌是否包含资源服务器期望的所有数据。

您的授权服务器和资源服务器配置看起来不错,但是在添加声明时您没有设置“目标”(不要忘记,为避免泄露机密数据,AspNet.Security.OpenIdConnect.Server 拒绝序列化声明) t 明确指定目的地):

var identity = new ClaimsIdentity(OpenIdConnectServerDefaults.AuthenticationScheme);
identity.AddClaim(ClaimTypes.Name, user.UserName, destination: "id_token token");

(await UM.GetRolesAsync(user)).ToList().ForEach(role => {
    identity.AddClaim(ClaimTypes.Role, role, destination: "id_token token");
});

另外,我注意到示例提供程序中注释掉了 OAuth Introspection,并且在某处读到 Jwt 不会很快得到支持。我找不到为我提供 OAuth Instrospection 的依赖项。

从下一个测试版开始(ASOS beta5,在写这个答案时还没有在 NuGet.org 上),我们将停止使用 JWT 作为访问令牌的默认格式,但当然,OTB 仍将支持 JWT。

现在默认情况下令牌是不透明的,您必须使用新的 validation middleware(灵感来自 Katana 的 OAuthBearerAuthenticationMiddleware)或实现 OAuth2 introspection RFC 的新标准 introspection middleware

app.UseOAuthValidation();

// Alternatively, you can also use the introspection middleware.
// Using it is recommended if your resource server is in a
// different application/separated from the authorization server.
// 
// app.UseOAuthIntrospection(options => {
//     options.AutomaticAuthenticate = true;
//     options.AutomaticChallenge = true;
//     options.Authority = "http://localhost:54540/";
//     options.Audience = "resource_server";
//     options.ClientId = "resource_server";
//     options.ClientSecret = "875sqd4s5d748z78z7ds1ff8zz8814ff88ed8ea4z4zzd";
// });

您可以在此处找到有关这 2 个中间件的更多信息:https://github.com/aspnet-contrib/AspNet.Security.OpenIdConnect.Server/issues/185

【讨论】:

  • 我从未听说过索赔目的地的概念。我会继续尝试添加它,看看会发生什么。我还将检查自省中间件。
  • 所以我明白了,目前OpenIdConnectServer生成了一个JWT,对,那么使用JwtBearerAuthentication对吗?即使为声明指定了目的地,我仍然收到 401。调试甚至没有到达我的控制器操作,所以我不能指定任何东西。
  • 如果您使用的是 NuGet.org 的最新版本,那么是的,JWT 仍然是默认格式。您应该发布整个 Startup 课程(以确保事情的顺序正确)。您还应该启用日志记录并发布调试跟踪。
  • 如何启用日志记录?我只运行我的应用程序的调试。我怎样才能看到为什么我会得到 401?我正在尝试设置断点并尝试进入函数,但 401 似乎击败了我到我需要去的地方。
猜你喜欢
  • 2016-01-28
  • 2016-10-09
  • 2019-07-31
  • 2023-03-18
  • 1970-01-01
  • 2019-05-02
  • 2018-07-19
  • 2021-03-01
  • 2016-06-14
相关资源
最近更新 更多