Я получаю следующий ответ при использовании Postman для отправки запроса GET с токеном носителя на мой API:
Статус: 401 Несанкционированный
WWW-Authenticate → Ошибка носителя = "invalid_token", error_description = "Ключ подписи не найден"
Мое решение включает в себя сервер авторизации под управлением IdentityServer4 и RESTful API, встроенный в ASP.NET Core 2.2
Сервер авторизации настроен с тестовым ресурсом, клиентом и пользователем. Он отвечает на запросы токенов, используя учетные данные тестового пользователя. Я получаю токен авторизации без проблем.
Мой API имеет один контроллер значений, украшенный фильтром действия [Авторизовать]. Перед добавлением фильтра он отвечал, как и ожидалось, на запросы GET. После этого отвечает 401 статус, когда токен не отправляется, но проблема в том, что я также получаю 401, когда я включаю токен в заголовок запроса. Я получаю ответ, в котором говорится, что ключ подписи не найден.
Я также настроил IdentityServer4 для входа в консоль, но на самом деле я не вижу зарегистрированных сообщений после ответа 401.
Решения, предлагаемые в стеке потока для подобных сценариев, не решили мою проблему Я подтвердил, что настроенное имя API соответствует client_id в моем заголовке запроса токена, в данном случае «logbookapi». Я отправляю токен, используя ключ заголовка Authorization, и добавляю токен в качестве значения «Носитель».
// API - Startup.cs
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.AddSingleton<IConfiguration>(Configuration);
services.AddScoped<ILogbookRepository, LogbookRepository>();
services.AddAuthentication("Bearer").AddIdentityServerAuthentication(options => new IdentityServerAuthenticationOptions
{
ApiName = "logbookapi",
Authority = "http://localhost:5000",
RequireHttpsMetadata = false
});
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
services.AddCors();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.UseCors(builder => builder.WithOrigins("http://localhost:6200/logbook")
.AllowAnyHeader()
.AllowAnyMethod()
.AllowAnyOrigin()
.AllowCredentials());
app.UseAuthentication();
app.UseMvc();
}
}
// Authorization Server - Startup.cs
public class Startup
{
// This method gets called by the runtime. Use this method to add services to the container.
// For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
public void ConfigureServices(IServiceCollection services)
{
services.AddIdentityServer()
.AddSigningCredential(new X509Certificate2(@"C:\Program Files\Git\usr\bin\logbookapi.pfx", "password"))
.AddInMemoryApiResources(InMemoryConfiguration.ApiResources())
.AddInMemoryClients(InMemoryConfiguration.Clients())
.AddTestUsers(InMemoryConfiguration.Users().ToList());
services.AddMvc();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.UseDeveloperExceptionPage();
app.UseIdentityServer();
app.UseStaticFiles();
app.UseMvcWithDefaultRoute();
}
}
// Authorization Server - in-memory config
public static class InMemoryConfiguration
{
public static IEnumerable<ApiResource> ApiResources()
{
return new[]
{
new ApiResource("logbookapi", "Logbook API")
};
}
public static IEnumerable<Client> Clients()
{
return new[]
{
new Client
{
ClientId = "w4lkr", // unique name for this in-memory client
ClientSecrets = new[]
{
new Secret("secret".Sha256()) // client secret hashed
},
AllowedGrantTypes = GrantTypes.ResourceOwnerPasswordAndClientCredentials, // allow authorization via username/password and authorization token
AllowedScopes = new[] // which scopes this client is allowed to use
{
"logbookapi"
}
}
};
}
public static IEnumerable<TestUser> Users()
{
return new[]
{
new TestUser
{
SubjectId = "1",
Username = "mail@curtwalker.com",
Password = "password"
}
};
}
}
// API - values controller
[Route("api/[controller]")]
[Authorize]
public class ValuesController : ControllerBase
{
// GET api/values
[HttpGet]
public ActionResult<IEnumerable<string>> Get()
{
return new string[] { "value1", "value2" };
}
// GET api/values/5
[HttpGet("{id}")]
public ActionResult<string> Get(int id)
{
return "value";
}
// POST api/values
[HttpPost]
public void Post([FromBody] string value)
{
}
// PUT api/values/5
[HttpPut("{id}")]
public void Put(int id, [FromBody] string value)
{
}
// DELETE api/values/5
[HttpDelete("{id}")]
public void Delete(int id)
{
}
}
Желаемый результат для ответа API:
{ "value1", "value2" }
как показано в контроллере значений.