带有 services.AddIdentity 的 WebApi 授权属性返回 404 Not Found [英] WebApi Authorize attribute with services.AddIdentity returns 404 Not Found
问题描述
我有一个简单的 WebApi 项目,它使用 IdentityServer4.AccessTokenValidation
来验证由 IdentityServer4
服务器在开发地址发出的令牌:https://localhost:44347
I have a simple WebApi project, which uses IdentityServer4.AccessTokenValidation
to validate tokens issued by an IdentityServer4
Server at development address: https://localhost:44347
我通过向身份服务器发送以下数据来获取令牌:
I get the token by sending following data to identityserver:
POST
https://localhost:44347/connect/token
client_id:x.api.client
client_secret:secret
response_type:code id_token
scope:X.api
grant_type:client_credentials
回复是:
{
"access_token": "THETOKEN",
"expires_in": 1209600,
"token_type": "Bearer"
}
并将令牌发送到 WebAPi
and sending the token to WebAPi
POST
http://localhost:59062/identity
Authorization:Bearer THETOKEN
我得到了想要的结果,但是,添加了以下代码结果 404 Not Found 的注释部分.
I get desired result, but, adding commented part of following code results 404 Not Found.
代码是:
public class Startup {
private const string API_NAME = "X.api";
public Startup(IConfiguration configuration) {
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services) {
string connectionString = Configuration.GetConnectionString("DefaultConnection");
services.AddLogging(configure => configure.AddConsole());
services.AddDbContext<MyDataContext>(options => options.UseSqlServer(connectionString));
services.AddMvcCore()
.AddAuthorization()
.AddJsonFormatters()
.SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
services.AddTransient<IUserStore<MyUser>, MyUserStore>();
services.AddTransient<IRoleStore<MyRole>, RoleStore>();
services.AddTransient<IPasswordHasher<MyUser>, MyHasher>();
services.AddAuthentication("Bearer")
.AddIdentityServerAuthentication(options => {
options.Authority = "https://localhost:44347";
options.RequireHttpsMetadata = false;
options.ApiName = API_NAME;
});
////This commented part brokes API
//services.AddIdentity<MyUser, MyRole>(options => {
// options.Password.RequireDigit = true;
// options.Password.RequiredLength = 6;
// options.Password.RequireNonAlphanumeric = false;
// options.Password.RequireUppercase = false;
// options.Password.RequireLowercase = false;
// options.SignIn.RequireConfirmedEmail = false;
//})
//Bekaz we are not using IdentityUser as base
//.AddUserStore<MyUserStore>()
//.AddRoleStore<RoleStore>()
//.AddDefaultTokenProviders();
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env) {
if (env.IsDevelopment()) {
app.UseDeveloperExceptionPage();
}
app.UseAuthentication();
app.UseMvc();
}
}
API 就像下面的一段代码一样简单(身份服务器的示例之一)
the API is as simple as following piece of code(one of identity server's samples)
using System.Linq;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Authorization;
namespace Api.Controllers {
[Route("[controller]")]
[Authorize]
public class IdentityController : ControllerBase {
[HttpGet]
public IActionResult Get() {
return new JsonResult(from c in User.Claims select new { c.Type, c.Value });
}
}
}
我使用从IIdentity
继承的自定义User类,自定义Role
和UserRole
,自定义RoleStore
实现IRoleStore
和自定义的 UserStore
实现了 IUserStore
.
I use custom User class inherited from IIdentity
, custom Role
and UserRole
, custom RoleStore
implemented IRoleStore<MyRole>
, and custom UserStore
implemented IUserStore<MyUser>, IUserPasswordStore<MyUser>
.
编辑,更多信息
这是我在控制台上得到的:
this is what i get on console:
info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1]
Request starting HTTP/1.1 GET http://localhost:5000/identity
info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1]
Route matched with {action = "Get", controller = "Identity"}. Executing action Api.Controllers.IdentityController.Get ()
info: Microsoft.AspNetCore.Authorization.DefaultAuthorizationService[2]
Authorization failed.
info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[3]
Authorization failed for the request at filter 'Microsoft.AspNetCore.Mvc.Authorization.AuthorizeFilter'.
info: Microsoft.AspNetCore.Mvc.ChallengeResult[1]
Executing ChallengeResult with authentication schemes ().
[16:48:20 Information] Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationHandler
AuthenticationScheme: Identity.Application was challenged.
info: Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationHandler[12]
AuthenticationScheme: Identity.Application was challenged.
info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2]
Executed action Api.Controllers.IdentityController.Get () in 30.1049ms
info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2]
Request finished in 103.2969ms 302
info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1]
Request starting HTTP/1.1 GET http://localhost:5000/Account/Login?ReturnUrl=%2Fidentity
info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2]
Request finished in 0.468ms 404
临时解决方案
授权系统
有些东西,我终于将属性更改为我在这里创建的内容
there is something with authorization system
, I finally changed attribute to what i founded here
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
它的工作原理.但如何以及为什么?我现在还没有.
and it works. but how and why? I don't now yet.
此外,将 AddAuthentication
部分更改为波纹管,正如前面提到的答案所暗示的那样,不 有效并且需要 (AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)
传递给 [Authorize]
Also, changing the AddAuthentication
part to bellow, as mentioned answer suggets, Does Not works and requires the (AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)
to be passed to [Authorize]
services.AddAuthentication(options => {
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddIdentityServerAuthentication(options => {
options.Authority = "https://localhost:44347";
options.RequireHttpsMetadata = false;
options.ApiName = API_NAME;
});
改变顺序,终于起作用了.(先AddIdentity
然后AddAuthentication
)
changing order, finally works.(first AddIdentity
and then AddAuthentication
)
services.AddIdentity<MyUser, MyRole>(options => {
options.Password.RequireDigit = true;
options.Password.RequiredLength = 6;
options.Password.RequireNonAlphanumeric = false;
options.Password.RequireUppercase = false;
options.Password.RequireLowercase = false;
options.SignIn.RequireConfirmedEmail = false;
})
.AddUserStore<MyUserStore>()
.AddRoleStore<RoleStore>();
services.AddAuthentication(options => {
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddIdentityServerAuthentication(options => {
options.Authority = "https://localhost:44347";
options.RequireHttpsMetadata = false;
options.ApiName = API_NAME;
});
推荐答案
让我试着解释一下,让其他一些可怜的人更容易理解:)
Let me try at explain this, so some other poor soul have some easier time understanding :)
当像上面那样添加身份验证时
When Authentications are added like above
services.AddAuthentication(options => {
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
})
....
这意味着放置在方法或控制器类之上的每个属性 [Authorize] 将尝试根据默认身份验证模式(在本例中为 JwtBearer)进行身份验证并且它不会向下级联 尝试与可能声明的其他模式(如 Cookie 模式)进行身份验证.为了使 AuthorizeAttribute 针对 cookie 模式进行身份验证,必须像上面的代码一样指定它
It means that every attribute [Authorize] that is put on top of a method or a controller class, will try to authenticate against the default authentication schema (in this case the JwtBearer) AND IT WILL NOT CASCADE DOWN to try to authenticate with other schemas that might be declared (like Cookie schema). In order to make the AuthorizeAttribute authenticate against the cookie schema it has to be specified like in code above
[Authorize(AuthenticationSchemes = CookieAuthenticationDefaults.AuthenticationScheme)]
这也可以反过来工作,即如果 cookie 模式是默认的,那么必须声明 JwtBearer 模式,以便为那些需要 JwtBearer 令牌身份验证的方法或控制器进行授权
This will work also the other way around, i.e. if cookie schema is default then the JwtBearer schema must be declared for authorization for those methods or controllers that would need JwtBearer token authentication
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
这篇关于带有 services.AddIdentity 的 WebApi 授权属性返回 404 Not Found的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!