在 API 上使用 Authorize 属性时,IdentityServer4 抛出 500 内部服务器错误

IdentityServer4 throws 500 internal server error when using Authorize attribute on API

我整个周末都在努力解决这个问题,但似乎无法让它发挥作用。请帮忙。

我已经设置了 IdentityServer 4、一个 MVC 应用程序和一个 API。 登录后我得到一个访问令牌,但是当我尝试访问 API 时它会抛出 500 内部服务器错误(如果我有授权​​属性)

这是我的身份服务器配置文件:

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

public static IEnumerable<ApiScope> ApiScopes =>
    new ApiScope[]
    {
        new ApiScope("api1", "my API")
    };


public static IEnumerable<Client> Clients =>
    new Client[]
    {

        new Client
        {
            ClientId = "mvc",
            ClientSecrets = { new Secret("secret".Sha256()) },

            AllowedGrantTypes = GrantTypes.Code,

            RedirectUris = { "https://localhost:5002/signin-oidc" },
            //FrontChannelLogoutUri = "https://localhost:5002/signout-oidc",
            PostLogoutRedirectUris = { "https://localhost:5002/signout-callback-oidc" },

            AllowOfflineAccess = true,
            AllowedScopes = new List<string>
            {
                IdentityServerConstants.StandardScopes.OpenId,
                IdentityServerConstants.StandardScopes.Profile,
                "api1" 
            }
        }
    };

这是identityServer启动class:

public IWebHostEnvironment Environment { get; }
    public IConfiguration Configuration { get; }

    public Startup(IWebHostEnvironment environment, IConfiguration configuration)
    {
        Environment = environment;
        Configuration = configuration;
    }

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddControllersWithViews();


        if (System.Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") == "Production")
            services.AddDbContext<ApplicationDbContext>(options =>
                    options.UseSqlServer(Configuration.GetConnectionString("MyDbConnection")));
        else
            services.AddDbContext<ApplicationDbContext>(options => options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));

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

        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;
        })
            .AddInMemoryIdentityResources(Config.IdentityResources)
            .AddInMemoryApiScopes(Config.ApiScopes)
            .AddInMemoryClients(Config.Clients)
            .AddAspNetIdentity<ApplicationUser>();

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

        services.AddAuthentication()
            .AddGoogle(options =>
            {
                options.SignInScheme = IdentityServerConstants.ExternalCookieAuthenticationScheme;

               
                options.ClientId = "xxx.apps.googleusercontent.com";
                options.ClientSecret = "xxx";

                options.ReturnUrlParameter = "https://xxx.azurewebsites.net/signin-google";
            }).AddFacebook(facebookOptions => 
            {
                facebookOptions.SignInScheme = IdentityServerConstants.ExternalCookieAuthenticationScheme;
                facebookOptions.ClientId = "xxx";
                facebookOptions.ClientSecret = "xxx";
                facebookOptions.ReturnUrlParameter = "https://xxx.azurewebsites.net/signin-facebook";
            });

    }

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

        app.UseStaticFiles();

        app.UseRouting();
        app.UseIdentityServer();

        app.UseAuthorization();
        app.UseEndpoints(endpoints =>
        {
            endpoints.MapDefaultControllerRoute();
        });
    }

这是我的 API 启动 class:

using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.IdentityModel.Tokens;
using System.Configuration;

namespace SF.API
{
public class Startup
{
    public Startup(IConfiguration configuration)
    {
        Configuration = configuration;
    }
    public IConfiguration Configuration
    {
        get;
    }
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddControllers();

        // accepts any access token issued by identity server
        services.AddAuthentication("Bearer")
            .AddJwtBearer("Bearer", options =>
            {
                options.Authority = ConfigurationManager.AppSettings["IdentityServerAddress"];
                
                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", "api1");
        //    });
        //});
    }

    public void Configure(IApplicationBuilder app)
    {
       
        app.UseRouting();
        app.UseAuthentication();
        app.UseAuthorization();

        app.UseEndpoints(endpoints =>
        {
            endpoints.MapControllerRoute("default", "{controller=Home}/{action=Index}/{id?}"); 
           
        });
        app.UseStaticFiles();

    }
}

}

这是控制器class:

[Route("identity")]
[Authorize]
public class IdentityController : ControllerBase
{
    public IActionResult Get()
    {
        return new JsonResult(from c in User.Claims select new { c.Type, c.Value });
    }
    [Route("/Test")]
    public IActionResult Test()
    {
        return new JsonResult(from c in User.Claims select new { c.Type, c.Value });
    }
}

这是MVC应用启动class:

 public void ConfigureServices(IServiceCollection services)
    {
        services.AddControllersWithViews();

        JwtSecurityTokenHandler.DefaultMapInboundClaims = false;

        services.AddAuthentication(options =>
        {
            options.DefaultScheme = "Cookies";
            options.DefaultChallengeScheme = "oidc";
        })
        .AddCookie("Cookies")
        .AddOpenIdConnect("oidc", options =>
        {
            

            options.Authority = "https://xxx.azurewebsites.net";
            options.ClientId = "mvc";
            options.ClientSecret = "secret";
            options.ResponseType = "code";

            options.SaveTokens = true;
            options.Scope.Add("api1");
            options.Scope.Add("offline_access");
        });
    }

    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }
        else
        {
            app.UseExceptionHandler("/Home/Error");
        }

        app.UseStaticFiles();

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

        app.UseEndpoints(endpoints =>
        {
            endpoints.MapDefaultControllerRoute()
                .RequireAuthorization();
        });
    }

我就是这样称呼 API:

 public async Task<IActionResult> CallApi()
    {
        var accessToken = await HttpContext.GetTokenAsync("access_token");

        var client = new HttpClient();
        client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
        var content = await client.GetStringAsync("https://localhost:6001/Test");
        ViewBag.Json = JArray.Parse(content).ToString();
        
        return View("json");
    }

我的访问令牌如下所示:

"eyJhbGciOiJSUzI1NiIsImtpZCI6IkExNDYxOUUzOTAwNjM5ODA2NUU4RkUwQjJFMkU1RThFIiwidHlwIjoiYXQrand0In0.eyJuYmYiOjE2NDg5ODEyMjMsImV4cCI6MTY0ODk4NDgyMywiaXNzIjoiaHR0cHM6Ly9pZGVudGl0eXNlcnZlcmlkZW50aXR5cHJvdmlkZXJzZi5henVyZXdlYnNpdGVzLm5ldCIsImF1ZCI6Imh0dHBzOi8vaWRlbnRpdHlzZXJ2ZXJpZGVudGl0eXByb3ZpZGVyc2YuYXp1cmV3ZWJzaXRlcy5uZXQvcmVzb3VyY2VzIiwiY2xpZW50X2lkIjoibXZjIiwic3ViIjoiZDc5NGQxOGUtNGYyOC00MmE3LWFkMTQtZDdiMWMxMDcwOTE5IiwiYXV0aF90aW1lIjoxNjQ4OTgxMjIzLCJpZHAiOiJsb2NhbCIsImp0aSI6IjZBRTE3Q0RBMjBGQkNGNDExQzc3QUIyQkNBNTE3M0YzIiwic2lkIjoiRkMxREY5QUZCODQyRkZEN0JGRjk5MTY0RTYyN0M2ODYiLCJpYXQiOjE2NDg5ODEyMjMsInNjb3BlIjpbIm9wZW5pZCIsInByb2ZpbGUiLCJhcGkxIiwib2ZmbGluZV9hY2Nlc3MiXSwiYW1yIjpbInB3ZCJdfQ.Grcu-dbrLy6LHHxsW2FJsSIhmwQBEl1jQ2LRvJhBFzZ8j0HAqk129Q8JncJFSFBjQkEls8xBFN-OxyvhJ5o7dmgpkgYENbfjl7jC04yhvSh_MzLqG2h_mme1mwsC3xzuKbQR1yczei-j92WUMeP-CvzUtr2vbJd2lJv0YvpJvykGF4BbKrQMPLPZnlLFRkPm5LcdFfUsrHrCz3R0JZ7tpVSwGMjGMlDHlAMAR04Fzf6YQhbKUEydNdTIWFP2akyBoWuRwAvTXbOA8vm9GZpeTbo8S4At5X7RhOR_J-zIjk1QWKhqN9kVMnMLXpO_NmZ6iQ66pcnT0G75rtFEfFtISQ"

我访问令牌的方式有什么问题吗?例如,我看不到示波器?

还有什么问题?

**更新:我在 API.Startup class 中添加了 app.UseAuthorization()。然后我得到了 401 Unauthroized

API 的一个问题是它缺少

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

在API启动中class。

要在ASP.NET Core 中调试API,您可以尝试将日志级别设置为top debug 或trace,就像在appsetttings 中这样:

{
  "Logging": {
    "LogLevel": {
      "Default": "Warning",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information",
      "Microsoft.AspNetCore.Authentication": "Trace",
      "Microsoft.AspNetCore.Authorization": "Trace"
    }
  }
}

您应该检查访问令牌 header:

{
  "alg": "RS256",
  "kid": "A14619E39006398065E8FE0B2E2E5E8E",
  "typ": "at+jwt"
}

儿童密钥标识符必须存在于 JWKS 端点中。