【问题标题】:WebApi Authorize attribute with services.AddIdentity returns 404 Not Found带有 services.AddIdentity 的 WebApi Authorize 属性返回 404 Not Found
【发布时间】:2018-12-18 05:16:51
【问题描述】:

我有一个简单的 WebApi 项目,它使用IdentityServer4.AccessTokenValidation 来验证由IdentityServer4 服务器发出的令牌,位于开发地址:https://localhost:44347

我通过向身份服务器发送以下数据来获取令牌:

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

POST
http://localhost:59062/identity
Authorization:Bearer THETOKEN

我得到了想要的结果,但是,添加以下代码结果 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 就像以下代码一样简单(身份服务器的示例之一)

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&lt;MyRole&gt;,自定义UserStore实现IUserStore&lt;MyUser&gt;, IUserPasswordStore&lt;MyUser&gt;

编辑,更多信息

这是我在控制台上得到的:

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

临时解决方案

authorization system有点东西,我终于把属性改成我founded here

[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]

它有效。但是如何以及为什么?我现在还没有。

此外,将AddAuthentication 部分更改为波纹管,如前面提到的答案建议, 有效,并且需要将(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme) 传递给[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

        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;
        });

【问题讨论】:

    标签: c# .net-core asp.net-identity identityserver4 asp.net-core-webapi


    【解决方案1】:

    让我试着解释一下,让其他一些可怜的灵魂更容易理解:)

    当像上面一样添加身份验证时

      services.AddAuthentication(options => {
                options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
                options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
                options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
            })
            ....
    

    这意味着放置在方法或控制器类之上的每个属性 [Authorize] 将尝试根据默认身份验证模式(在本例中为 JwtBearer)进行身份验证并且它不会级联 尝试使用可能声明的其他模式(如 Cookie 模式)进行身份验证。为了使 AuthorizeAttribute 对 cookie 模式进行身份验证,必须像上面的代码一样指定它

    [Authorize(AuthenticationSchemes = CookieAuthenticationDefaults.AuthenticationScheme)]
    

    这也可以反过来工作,即如果 cookie 模式是默认的,则必须声明 JwtBearer 模式以授权那些需要 JwtBearer 令牌身份验证的方法或控制器

    [Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
    

    【讨论】:

    • 好消息。谢谢!
    • 再次,在 4 年后,这拯救了我的夜晚。
    猜你喜欢
    • 1970-01-01
    • 2015-06-09
    • 1970-01-01
    • 2016-03-16
    • 2021-06-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多