首页 > 解决方案 > Identity Server 4 Postman 请求令牌不起作用

问题描述

我有一个 API,我正在使用 IdentityServer4 进行身份验证。所以,我有一个解决方案,其中包含我的 API 和 Identity Server 项目。到现在为止还挺好。我已经在没有身份验证的情况下测试了 API,它工作正常。现在我正在尝试使用 Postman 使用身份验证对其进行测试,但遇到了问题。我正在尝试使用授权代码授权类型请求令牌,并且我输入了所有需要的信息。我在控制台中不断遇到的错误是

09:51:31 信息] IdentityServer4.ResponseHandling.AuthorizeInteractionResponseGenerator 显示登录:用户未通过身份验证

我在网上查找了有关如何设置 API 和 Identity Server4 的教程,但他们都想创建一个客户端并显示一个登录屏幕。我不需要登录这个 API,而且我不知道我的客户会做什么。我想要的只是让客户端能够使用 clientID 和客户端 Secret 进行授权。然后获取他们可以用于其余 API 调用的访问令牌。我怎样才能做到这一点?下面是我的 API 和 IDP 的启动

API 启动

 public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddControllers();
            services.AddHttpContextAccessor();
            
            services.AddAuthentication("Bearer")
            .AddJwtBearer("Bearer", options =>
            {
                options.Authority = "https://localhost:5001";

                options.TokenValidationParameters = new TokenValidationParameters
                {
                    ValidateAudience = false
                };
            });

            // adds an authorization policy to make sure the token is for scope 'api1'
            services.AddAuthorization(options =>
            {
                options.AddPolicy("ApiScope", policy =>
                {
                    policy.RequireAuthenticatedUser();
                    policy.RequireClaim("scope", "URCSAPI");
                });
            });
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }

            app.UseHttpsRedirection();

            app.UseRouting();

            app.UseAuthentication();

            app.UseAuthorization();

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllers();
            });
        }
    }

IDP 启动

 public class Startup
    {
        public IWebHostEnvironment Environment { get; }

        public Startup(IWebHostEnvironment environment)
        {
            Environment = environment;
        }

        public void ConfigureServices(IServiceCollection services)
        {
            var URCSIDPDataDBConnectionString =
                "Server=localhost;Database=URCSIDP;Trusted_Connection=True;MultipleActiveResultSets=true";

            // uncomment, if you want to add an MVC-based UI
            services.AddControllersWithViews();

            var builder = services.AddIdentityServer();
                //.AddInMemoryIdentityResources(Config.Ids)
                //.AddInMemoryApiResources(Config.Apis)
                //.AddInMemoryClients(Config.Clients)
                //.AddTestUsers(TestUsers.Users);

            // not recommended for production - you need to store your key material somewhere secure
            builder.AddDeveloperSigningCredential();
            //builder.AddSigningCredential(LoadCertificateFromStore());

            var migrationsAssembly = typeof(Startup)
                .GetTypeInfo().Assembly.GetName().Name;

            builder.AddConfigurationStore(options =>
            {
                options.ConfigureDbContext = builder => 
                    builder.UseSqlServer(URCSIDPDataDBConnectionString,
                    options => options.MigrationsAssembly(migrationsAssembly));
            });

            builder.AddOperationalStore(options =>
            {
                options.ConfigureDbContext = builder => 
                    builder.UseSqlServer(URCSIDPDataDBConnectionString,
                    options => options.MigrationsAssembly(migrationsAssembly));
            });
        }

        public void Configure(IApplicationBuilder app)
        {
            if (Environment.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }

            //InitializeDatabase(app);

            // uncomment if you want to add MVC
            //app.UseStaticFiles();
            //app.UseRouting();

            app.UseIdentityServer();

            // uncomment, if you want to add MVC
            //app.UseAuthorization();
            //app.UseEndpoints(endpoints =>
            //{
            //    endpoints.MapDefaultControllerRoute();
            //});
        }

        public X509Certificate2 LoadCertificateFromStore()
        {
            string thumbPrint = "d4d681b3de4cd26fc030292aeea170e553810bdb";

            using (var store = new X509Store(StoreName.My, StoreLocation.LocalMachine))
            {
                store.Open(OpenFlags.ReadOnly);
                var certCollection = store.Certificates.Find(X509FindType.FindByThumbprint,
                    thumbPrint, true);
                if (certCollection.Count == 0)
                {
                    throw new Exception("The specified certificate wasn't found.");
                }
                return certCollection[0];
            }
        }

    }

更新:添加我用来将信息添加到数据库的配置文件:

 public static class Config
    {
        public static IEnumerable<IdentityResource> Ids =>
            new IdentityResource[]
            {
                new IdentityResources.OpenId(),
                new IdentityResources.Profile(),
                new IdentityResources.Address()
            };

        public static IEnumerable<ApiResource> Apis =>
            new ApiResource[]
            {
                new ApiResource(
                    "URCSAPI",
                    "Unit Rate ContractSystem API")
                {
                    ApiSecrets = { new Secret("apisecret".Sha256()) }
                }
            };

        public static IEnumerable<Client> Clients =>
            new Client[]
            {
                new Client
                {
                    AccessTokenType = AccessTokenType.Reference,
                    AccessTokenLifetime = 120,
                    AllowOfflineAccess = true,
                    UpdateAccessTokenClaimsOnRefresh = true,
                    ClientName = "Tesla",
                    ClientId = "Tesla",
                    AllowedGrantTypes = GrantTypes.ClientCredentials,
                    RequirePkce = false,
                    RedirectUris = new List<string>()
                    {
                        "https://localhost:6001/signin-oidc"
                    },
                    PostLogoutRedirectUris = new List<string>()
                    {
                        "https://localhost:6001/signout-callback-oidc"
                    },
                    AllowedScopes =
                    {
                        IdentityServerConstants.StandardScopes.OpenId,
                        IdentityServerConstants.StandardScopes.Profile,
                        IdentityServerConstants.StandardScopes.Address,
                        "URCSAPI"
                    },
                    ClientSecrets =
                    {
                        new Secret("secret".Sha256())
                    }
                }
            };
    }

标签: apipostmanidentityserver4

解决方案


如果您试图保护未与交互式界面一起使用的 API(例如,用户在浏览器中的网站),那么验证代码流不是您想要使用的方法。Auth Code 流程是一个“交互式”流程,这意味着用户通过浏览器或移动应用程序在表单上登录。如果您的 API 是为机器对机器使用而设计的,或者其他人正在编写代码以直接访问您的 API,那么您应该使用客户端凭据流。此流程仅传递 clientId 和 clientSecret 以获取访问令牌以用于 API。

请参阅 ID4 网站上的此页面: https ://docs.identityserver.io/en/latest/quickstarts/1_client_credentials.html

查看您的 IdentityServer Config.cs 并确保其中的所有内容都是正确的。对于测试,使用 InMemory 存储(取消注释“AddInMemory”行)而不是 DB 中基于 EF 的存储更容易(您可以更快地进行调整,然后稍后移动到基于 DB 的存储)。

您将需要一个类型为“ClientCredentials”的客户端(请参阅上面 URL 中的“定义客户端”部分)。他们提供的基本示例如下所示:

public static IEnumerable<Client> Clients =>
new List<Client>
{
    new Client
    {
        ClientId = "client",

        // no interactive user, use the clientid/secret for authentication
        AllowedGrantTypes = GrantTypes.ClientCredentials,

        // secret for authentication
        ClientSecrets =
        {
            new Secret("secret".Sha256())
        },

        // scopes that client has access to
        AllowedScopes = { "api1" }
    }
};

推荐阅读