首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >IdentityServer4、本地应用程序接口、外部REST客户端

IdentityServer4、本地应用程序接口、外部REST客户端
EN

Stack Overflow用户
提问于 2020-12-10 23:57:17
回答 2查看 220关注 0票数 0

我正在使用IdentityServer4 NuGet包在我的asp.net核心服务器应用程序中设置IdentityServer。同一个应用程序托管一个受保护的应用程序接口,在通过服务器应用程序的IdentityServer部分进行身份验证后,应该可以通过REST客户端访问该应用程序。我在服务器上的本地认证/授权起作用了。此外,客户端还能够从服务器获得有效的令牌。但是当在服务器上调用API时,我得到一个“未授权”的错误,并且API返回登录表单。有没有人观察到同样的情况?哪里出了问题?

运行IdentityServer和API的ServerApp

代码语言:javascript
复制
        public void ConfigureServices(IServiceCollection services)
    {
        services.AddLogging(loggingBuilder =>
        {
            loggingBuilder.AddConsole().AddFilter(DbLoggerCategory.Database.Command.Name, LogLevel.Information);
            loggingBuilder.AddDebug();
        });

        // For Entity Framework  
        services.AddDbContextPool<ApplicationDbContext>(options =>
        {
            options.UseLazyLoadingProxies()
                .UseMySql(Configuration.GetConnectionString("ConnStr"), MySQLServerVersion.ServerVersion);
            options.EnableSensitiveDataLogging();
        });

        services.AddIdentity<ApplicationUser, IdentityRole>()
            .AddEntityFrameworkStores<ApplicationDbContext>()
            .AddDefaultTokenProviders();
        //.AddDefaultUI();

        services.AddTransient<IEmailSender, EmailSender>();

        services.AddControllersWithViews();
        services.AddRazorPages().AddRazorRuntimeCompilation();

        var migrationsAssembly = typeof(Startup).GetTypeInfo().Assembly.GetName().Name;
        // configure identity server with mysql stores, keys, clients and scopes
        var builder = services.AddIdentityServer(options =>
            {
                options.Events.RaiseErrorEvents = true;
                options.Events.RaiseInformationEvents = true;
                options.Events.RaiseFailureEvents = true;
                options.Events.RaiseSuccessEvents = true;

                // see https://identityserver4.readthedocs.io/en/latest/topics/resources.html
                options.EmitStaticAudienceClaim = true;
            })
            .AddAspNetIdentity<ApplicationUser>()
            // this adds the config data from DB (clients, resources)
            .AddConfigurationStore(options =>
            {
                options.ConfigureDbContext = b =>
                    b.UseMySql(Configuration.GetConnectionString("ConnStr"), MySQLServerVersion.ServerVersion,
                        sql => sql.MigrationsAssembly(migrationsAssembly));
            })
            // this adds the operational data from DB (codes, tokens, consents)
            .AddOperationalStore(options =>
            {
                options.ConfigureDbContext = b =>
                    b.UseMySql(Configuration.GetConnectionString("ConnStr"), MySQLServerVersion.ServerVersion,
                        sql => sql.MigrationsAssembly(migrationsAssembly));

                // this enables automatic token cleanup. this is optional.
                options.EnableTokenCleanup = true;
                options.TokenCleanupInterval = 30;
            })
            .AddJwtBearerClientAuthentication()
            .AddProfileService<ProfileService>();

        services.AddLocalApiAuthentication();
        services.AddAuthentication(IdentityServerAuthenticationDefaults.AuthenticationScheme)
            .AddIdentityServerAuthentication(options =>
            {
                // base-address of your identityserver
                options.Authority = "https://localhost:5001";

                // name of the API resource
                options.ApiName = "api1";
            });
        services.AddAuthorization(options =>
        {
            options.AddPolicy("ApiScope", policy =>
            {
                policy.RequireAuthenticatedUser();
                policy.RequireClaim("scope", "api1");
            });
        });

        services.AddTransient<IProfileService, ProfileService>();

        // not recommended for production - you need to store your key material somewhere secure
        //builder.AddDeveloperSigningCredential();
        var fileName = Path.Combine("cert.pfx");
        if (!File.Exists(fileName))
        {
            throw new FileNotFoundException("Signing Certificate is missing!");
        }

        var cert = new X509Certificate2(fileName, "secret");
        builder.AddSigningCredential(cert);

        // Register the Swagger services
        services.AddSwaggerDocument(options =>
        {
            options.Title = "TEST";
            options.AddSecurity("oauth2", new OpenApiSecurityScheme
            {
                Type = OpenApiSecuritySchemeType.OAuth2,
                Flows = new OpenApiOAuthFlows
                {
                    AuthorizationCode = new OpenApiOAuthFlow
                    {
                        AuthorizationUrl = "https://localhost:5001/connect/authorize",
                        TokenUrl = "https://localhost:5001/connect/token",
                        Scopes = new Dictionary<string, string> { { "api1", "Demo API - full access" } }
                    }
                }
            });
            options.OperationProcessors.Add(new OperationSecurityScopeProcessor("oauth2"));
        });
    }

    // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        // this will do the initial DB population
        app.InitializeDatabase();

        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
            //app.UseBrowserLink();
            app.UseDatabaseErrorPage();
            app.UseMigrationsEndPoint();
        }
        else
        {
            app.UseExceptionHandler("/Home/Error");
            // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
            app.UseHsts();
        }

        app.UseHttpsRedirection();
        app.UseStaticFiles();

        app.UseRouting();

        // app.UseAuthentication(); // not needed, since UseIdentityServer adds the authentication middleware
        app.UseIdentityServer();
        app.UseAuthorization();

        app.UseEndpoints(endpoints =>
        {
            endpoints.MapControllers();
            endpoints.MapControllerRoute(
                name: "default",
                pattern: "{controller=Home}/{action=Index}/{id?}");
            endpoints.MapRazorPages();
        });

        // Register the Swagger generator and the Swagger UI middlewares
        app.UseOpenApi();
        app.UseSwaggerUi3(options =>
        {
            options.DocumentTitle = "DEMO";
            options.OAuth2Client = new OAuth2ClientSettings
            {
                ClientId = "demo_api_swagger",
                AppName = "Demo API - Swagger",
                UsePkceWithAuthorizationCodeGrant = true
            };
        });
    }

Config.cs

代码语言:javascript
复制
public static class Config
{
    public static IEnumerable<IdentityResource> IdentityResources =>
        new List<IdentityResource>
        {
            new IdentityResources.OpenId(),
            new IdentityResources.Profile()
        };


    public static IEnumerable<ApiScope> ApiScopes =>
        new List<ApiScope>
        {
            new ApiScope("api1", "My API")
        };

    public static IEnumerable<ApiResource> ApiResources =>
        new List<ApiResource>
        {
            new ApiResource("api1", "My API")
        };

    public static IEnumerable<Client> Clients =>
        new List<Client>
        {
            // machine to machine client
            new Client
            {
                ClientId = "client",
                ClientSecrets = {new Secret("secret".Sha256())},

                AllowedGrantTypes = GrantTypes.ClientCredentials,
                // scopes that client has access to
                AllowedScopes = {"api1"}
            },

            // external user login
            new Client
            {
                ClientId = "external",
                ClientSecrets = {new Secret("secret".Sha256())},

                AllowedGrantTypes = GrantTypes.ResourceOwnerPassword,
                // scopes that client has access to
                AllowedScopes = {"api1"}
            },

            // interactive ASP.NET Core MVC client
            new Client
            {
                ClientId = "mvc",
                ClientSecrets = {new Secret("secret".Sha256())},

                AllowedGrantTypes = GrantTypes.Code,

                // where to redirect to after login
                RedirectUris =
                {
                    "https://localhost:5003/signin-oidc",
                },
                // where to redirect to after logout
                PostLogoutRedirectUris =
                {
                    "https://localhost:5003/signout-callback-oidc",
                },

                // scopes that client has access to
                AllowedScopes =
                {
                    IdentityServerConstants.StandardScopes.OpenId,
                    IdentityServerConstants.StandardScopes.Profile,
                    "api1"
                }
            },

            //swagger
            new Client
            {
                ClientId = "demo_api_swagger",
                ClientName = "Swagger UI for demo_api",
                ClientSecrets = {new Secret("secret".Sha256())}, // change me!

                AllowedGrantTypes = GrantTypes.Code,
                RequirePkce = false,
                RequireClientSecret = false,

                // where is it allowed to redirect to after login
                RedirectUris =
                {
                    "https://localhost:5001/swagger/oauth2-redirect.html",
                    "https://localhost:5003/swagger/oauth2-redirect.html"
                },
                AllowedCorsOrigins =
                {
                    "https://localhost:5001",
                    "https://localhost:5003"
                },
                AllowedScopes = {"api1"}
            }
        };
}

服务器应用程序中的标记控制器

代码语言:javascript
复制
[Authorize("ApiScope")]
[ApiController]
[Route("[controller]")]
public class TestController : ControllerBase

和客户端应用程序

代码语言:javascript
复制
        public void ConfigureServices(IServiceCollection services)
    {
        //Session data
        services.AddDistributedMemoryCache();
        services.AddSession(options =>
        {
            options.Cookie.Name = "MVCClient";
            options.IdleTimeout = TimeSpan.FromSeconds(10);
            options.Cookie.HttpOnly = true;
            options.Cookie.IsEssential = true;
        });

        JwtSecurityTokenHandler.DefaultMapInboundClaims = false;
        services.AddAuthentication(options =>
            {
                options.DefaultScheme = "Cookies";
                options.DefaultChallengeScheme = "oidc";
            })
            .AddCookie("Cookies", options =>
            {
                options.ExpireTimeSpan = new TimeSpan(12, 0, 0, 0);
            })
            .AddOpenIdConnect("oidc", options =>
            {
                options.Authority = "https://localhost:5001";

                options.ClientId = "mvc";
                options.ClientSecret = "secret";
                options.ResponseType = "code";

                options.Scope.Add("api1");

                options.GetClaimsFromUserInfoEndpoint = true;
                options.SaveTokens = true;

                options.ClaimActions.Add(new JsonKeyClaimAction(JwtClaimTypes.Role, null, "role"));
                options.ClaimActions.Add(new JsonKeyClaimAction("isactive", null, "isactive"));
                options.ClaimActions.Add(new JsonKeyClaimAction("title", null, "title"));

                options.TokenValidationParameters = new TokenValidationParameters
                {
                    NameClaimType = JwtClaimTypes.Name,
                    RoleClaimType = JwtClaimTypes.Role
                };
            });

        // 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", "api1");
            });
        });

        services.AddControllersWithViews();

        services.AddHttpContextAccessor();
        services.AddHttpClient("TheClient", c =>
        {
            c.BaseAddress = new Uri("https://localhost:5001");

            // access the DI container
            var serviceProvider = services.BuildServiceProvider();
            // Find the HttpContextAccessor service
            var httpContextAccessor = serviceProvider.GetService<IHttpContextAccessor>();
            // Get the bearer token from the request context (header)
            var bearerToken = httpContextAccessor?.HttpContext?.GetTokenAsync("access_token").Result;
            c.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", bearerToken);
        });

        services.AddRazorPages().AddRazorRuntimeCompilation();
        services.RegisterDataTables();
        services.AddExpressiveAnnotations();
    }

这就是我请求客户端应用程序(控制台应用程序)时获得的令牌的内容:TOKEN my ConsoleApp got

EN

回答 2

Stack Overflow用户

发布于 2020-12-11 02:29:52

在IDS4配置中更改ApiResources并添加作用域。如下所示:

代码语言:javascript
复制
public static IEnumerable<ApiResource> ApiResources =>
        new List<ApiResource>
        {
            new ApiResource("api1", "My API")
            {
               Scopes = { "api1" }
            }
        };

完成此更改后,请验证https://jwt.io/上的access_token,现在您应该在令牌中具有作为api1aud

票数 0
EN

Stack Overflow用户

发布于 2020-12-14 22:58:21

代码语言:javascript
复制
public static void InitializeDatabase(this IApplicationBuilder app)
    {
        using var serviceScope = app.ApplicationServices.GetService<IServiceScopeFactory>()?.CreateScope();
        serviceScope.ServiceProvider.GetRequiredService<PersistedGrantDbContext>().Database.Migrate();

        var configurationDbContext = serviceScope.ServiceProvider.GetRequiredService<ConfigurationDbContext>();
        configurationDbContext.Database.Migrate();

        if (!configurationDbContext.Clients.Any())
        {
            foreach (var client in Config.Clients)
            {
                configurationDbContext.Clients.Add(client.ToEntity());
            }
            configurationDbContext.SaveChanges();
        }

        if (!configurationDbContext.IdentityResources.Any())
        {
            foreach (var resource in Config.IdentityResources)
            {
                configurationDbContext.IdentityResources.Add(resource.ToEntity());
            }
            configurationDbContext.SaveChanges();
        }

        if (!configurationDbContext.ApiScopes.Any())
        {
            foreach (var resource in Config.ApiScopes)
            {
                configurationDbContext.ApiScopes.Add(resource.ToEntity());
            }
            configurationDbContext.SaveChanges();
        }

        if (!configurationDbContext.ApiResources.Any())
        {
            foreach (var resource in Config.ApiResources)
            {
                configurationDbContext.ApiResources.Add(resource.ToEntity());
            }
            configurationDbContext.SaveChanges();
        }

        var applicationDbContextDbContext = serviceScope.ServiceProvider.GetRequiredService<ApplicationDbContext>();
        applicationDbContextDbContext.Database.Migrate();
        serviceScope.ServiceProvider.GetRequiredService<RoleManager<IdentityRole>>().SeedRoles();
        serviceScope.ServiceProvider.GetRequiredService<UserManager<ApplicationUser>>().SeedUsers();
    }

    public static void SeedRoles(this RoleManager<IdentityRole> roleManager)
    {
        if (!roleManager.RoleExistsAsync(UserRoles.Admin).Result)
        {
            var role = new IdentityRole { Name = UserRoles.Admin, NormalizedName = UserRoles.Admin.ToUpper() };
            var roleResult = roleManager.CreateAsync(role).Result;
            if (!roleResult.Succeeded)
            {
                throw new Exception(roleResult.Errors.First().Description);
            }
        }

        if (!roleManager.RoleExistsAsync(UserRoles.SuperUser).Result)
        {
            var role = new IdentityRole
            { Name = UserRoles.SuperUser, NormalizedName = UserRoles.SuperUser.ToUpper() };
            var roleResult = roleManager.CreateAsync(role).Result;
            if (!roleResult.Succeeded)
            {
                throw new Exception(roleResult.Errors.First().Description);
            }
        }

        if (!roleManager.RoleExistsAsync(UserRoles.User).Result)
        {
            var role = new IdentityRole { Name = UserRoles.User, NormalizedName = UserRoles.User.ToUpper() };
            var roleResult = roleManager.CreateAsync(role).Result;
            if (!roleResult.Succeeded)
            {
                throw new Exception(roleResult.Errors.First().Description);
            }
        }
    }

    public static void SeedUsers(this UserManager<ApplicationUser> userManager)
    {
        if (userManager.FindByNameAsync("admin").Result == null)
        {
            var user = new ApplicationUser
            { UserName = "admin", NormalizedUserName = "ADMIN", Email = "test@localhost.lan", FirstName = "X", LastName = "Y", IsActive = true };
            var result = userManager.CreateAsync(user, "secret").Result;
            if (!result.Succeeded)
            {
                throw new Exception(result.Errors.First().Description);
            }
            result = userManager.AddClaimsAsync(user, new[]
            {
                new Claim("isactive", user.IsActive.ToString()),
                new Claim(JwtClaimTypes.Name, user.UserName),
                new Claim(JwtClaimTypes.GivenName, user.FirstName),
                new Claim(JwtClaimTypes.FamilyName, user.LastName),
                new Claim(JwtClaimTypes.Email, user.Email),
                new Claim("scope", "api1")
            }).Result;
            if (!result.Succeeded)
            {
                throw new Exception(result.Errors.First().Description);
            }
            result = userManager.AddToRoleAsync(user, UserRoles.Admin).Result;
            if (!result.Succeeded)
            {
                throw new Exception(result.Errors.First().Description);
            }
        }
    }
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/65237936

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档