带有 services.AddIdentity 的 WebApi 授权属性返回 404 Not Found [英] WebApi Authorize attribute with services.AddIdentity returns 404 Not Found

查看:27
本文介绍了带有 services.AddIdentity 的 WebApi 授权属性返回 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类,自定义RoleUserRole,自定义RoleStore实现IRoleStore 和自定义的 UserStore 实现了 IUserStore、IUserPasswordStore.

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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆