我正在使用IdentityServer4 NuGet包在我的asp.net核心服务器应用程序中设置IdentityServer。同一个应用程序托管一个受保护的应用程序接口,在通过服务器应用程序的IdentityServer部分进行身份验证后,应该可以通过REST客户端访问该应用程序。我在服务器上的本地认证/授权起作用了。此外,客户端还能够从服务器获得有效的令牌。但是当在服务器上调用API时,我得到一个“未授权”的错误,并且API返回登录表单。有没有人观察到同样的情况?哪里出了问题?
运行IdentityServer和API的ServerApp
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
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"}
}
};
}服务器应用程序中的标记控制器
[Authorize("ApiScope")]
[ApiController]
[Route("[controller]")]
public class TestController : ControllerBase和客户端应用程序
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
发布于 2020-12-11 02:29:52
在IDS4配置中更改ApiResources并添加作用域。如下所示:
public static IEnumerable<ApiResource> ApiResources =>
new List<ApiResource>
{
new ApiResource("api1", "My API")
{
Scopes = { "api1" }
}
};完成此更改后,请验证https://jwt.io/上的access_token,现在您应该在令牌中具有作为api1的aud
发布于 2020-12-14 22:58:21
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);
}
}
}https://stackoverflow.com/questions/65237936
复制相似问题