无法让 Identity Server 4 API 授权其自己的端点,而是发送登录页面

Posted

技术标签:

【中文标题】无法让 Identity Server 4 API 授权其自己的端点,而是发送登录页面【英文标题】:Can't get Identity Server 4 API to authorize its own endpoints, sends login page instead 【发布时间】:2021-08-28 11:35:08 【问题描述】:

目前有效

我有两个 ASP.NET Core API:

    一个业务 API,管理业务实体(想想产品、菜单等) 一个 IS4 API,管理用户和身份验证,并用于授权用户访问第一个

我是如何做到的:我在第二个 API 上的控制器是使用 Authorize(Roles = "Admin") 授权的,因此只有管理员可以访问它。示例:

[Authorize(Roles = "Admin")]
[ApiController]
[Route("[controller]")]
public class MenuController 
   ...

基本上,我的前端使用 IS4 API 登录,它返回一个 JWT,而该 JWT 又用于向业务 API 验证自身。在我的 Startup.cs 业务 API 中,我有这个:

public void ConfigureServices(IServiceCollection services)

    services.AddCors(options => options.AddPolicy("AllowAll", p => p.AllowAnyOrigin().AllowAnyMethod().AllowAnyHeader()));
    
    services.AddAuthentication("Bearer")
        .AddJwtBearer("Bearer", options =>
        
            options.RequireHttpsMetadata = false;
        options.Authority = "http://localhost:5000";
        options.TokenValidationParameters = new TokenValidationParameters
        
            ValidateAudience = false
        ;
    );
    
    services.AddApplication();
    services.AddInfrastructure(Configuration);
    services.AddControllers();


public void Configure(IApplicationBuilder app, IWebHostEnvironment env)

    app.UseCors("AllowAll");

    if (env.IsDevelopment())
    
        app.UseDeveloperExceptionPage();
    

    app.UseHttpsRedirection();

    app.UseRouting();

    app.UseAuthentication();
    app.UseAuthorization();

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

我的问题

以上工作正常。我试图在我的 IS4 API 的某些端点上做同样的事情,以便为管理员用户保留一些操作(删除用户、注册用户)。这是我为此目的在控制器中创建的一个基本端点:

[Authorize]
[HttpGet]
[Route("api/[controller]/debug")]
public async Task<IActionResult> DebugEndpoint()

    return Ok("debug ok");

我也在Startup.cs注册了API作为自己的权限,和第一个API一样:

public void ConfigureServices(IServiceCollection services)

    services.AddCors(options => options.AddPolicy("AllowAll", p => p.AllowAnyOrigin()
        .AllowAnyMethod()
        .AllowAnyHeader()));
    
    services.AddHealthChecks();

    services.AddIdentity<AppUser, IdentityRole>()
        .AddEntityFrameworkStores<AppIdentityDbContext>()
        .AddDefaultTokenProviders();

    services.AddIdentityServer()
        .AddDeveloperSigningCredential()
        .AddOperationalStore(options =>
        
            options.ConfigureDbContext = builder => builder.Usemysql(Configuration.GetConnectionString("Default"));
            options.EnableTokenCleanup = true;
            options.TokenCleanupInterval = 30; // interval in seconds
        )
        .AddInMemoryIdentityResources(Config.GetIdentityResources())
        .AddInMemoryApiResources(Config.GetApiResources())
        .AddInMemoryClients(Config.GetClients())
        .AddTestUsers(Config.TestUsers)
        .AddAspNetIdentity<AppUser>();

    services.AddLocalApiAuthentication();
    
    services.AddAuthorization(options =>
    
        options.AddPolicy("Admin", policy =>
        
            var is4Policy =  options.GetPolicy(IdentityServerConstants.LocalApi.PolicyName);
            policy.Combine(is4Policy);
            policy.RequireClaim(ClaimTypes.Role, "Admin");
        );
    );
    

    services.AddTransient<IProfileService, IdentityClaimsProfileService>();

    services.AddAuthentication("Bearer")
        .AddJwtBearer("Bearer", options =>
        
            options.RequireHttpsMetadata = false;
            options.Authority = "http://localhost:5000";
            options.TokenValidationParameters = new TokenValidationParameters
            
                ValidateAudience = false
            ;
        );


    services.AddMvc(options =>
    
        options.EnableEndpointRouting = false;
    ).SetCompatibilityVersion(CompatibilityVersion.Latest);



public static void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILoggerFactory loggerFactory)

    app.UseExceptionHandler(builder =>
    
        builder.Run(async context =>
        
            context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
            context.Response.Headers.Add("Access-Control-Allow-Origin", "*");

            var error = context.Features.Get<IExceptionHandlerFeature>();
            if (error != null)
            
                context.Response.AddApplicationError(error.Error.Message);
                await context.Response.WriteAsync(error.Error.Message).ConfigureAwait(false);
            
        );
    );

    app.UseAuthorization();
    app.UseCookiePolicy(new CookiePolicyOptions  MinimumSameSitePolicy = SameSiteMode.None );
    app.UseStaticFiles();
    app.UseCors("AllowAll");
    app.UseIdentityServer();
    app.UseAuthentication();
    
    app.UseMvc(routes =>
    
        routes.MapRoute(
            name: "default",
            template: "controller=Home/action=Index/id?");
    );

预期结果

如果我已登录,或者当我在注释中添加(Roles = "Admin") 时具有特定角色,我应该只能访问调试端点并获得“debug ok”。

实际结果

除非我删除 [Authorize] 注释,否则它总是让我再次调用登录页面。我在授权中传递了访问令牌,就像我对另一个 API 所做的那样,但是当它授权我使用另一个 API 时,它没有,就好像我什至没有传递访问令牌一样,我不明白为什么。

【问题讨论】:

你是从 VS 运行的吗? VS 不会自动以 ADMIN 开头。您需要右键单击 VS 快捷方式并选择以管理员身份运行。 这不是真正的 Windows 管理员状态,它只是 JWT 中传递的声明。 【参考方案1】:

我必须将此添加到我的端点。

[Authorize(AuthenticationSchemes="Bearer")]

【讨论】:

以上是关于无法让 Identity Server 4 API 授权其自己的端点,而是发送登录页面的主要内容,如果未能解决你的问题,请参考以下文章

Identity Server 4 从入门到落地—— 创建Web Api

无法让分布式缓存与 Identity Server 一起使用

Identity Server 4 - Hybrid Flow - 保护API资源

Identity Server 4 的 API 授权不断返回 401 Unauthorized

.Net Core 自定义身份验证使用 API 密钥和 Identity Server 4

如何使用 Identity Server 4 (JWT) 进行基于角色的 Web API 授权