API授权使用Identity Server 4始终返回401未经授权。

8

我正在使用 Identity Server 4 .Net Core 3,如果我在启动文件中使用标准配置,我的API端点不会验证访问令牌,我一直收到401未授权的错误提示。但是当我在控制器中使用authorize属性来设置身份验证方案时,我可以使用相同的令牌成功访问我的端点...

[Route("api/[controller]")]
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
[ApiController]
public class MyWebAPiControllerController : ControllerBase
{
.......

以下是我的身份验证服务器配置:

//API resource       
public IEnumerable<ApiResource> Apis()
{
        var resources = new List<ApiResource>();

        resources.Add(new ApiResource("identity", "My API", new[] { JwtClaimTypes.Subject, JwtClaimTypes.Email, JwtClaimTypes.Role, JwtClaimTypes.Profile }));

        return resources;
}

我的客户端配置:

public IEnumerable<Client> Clients()
    {

        var Clients = new List<Client>();

        Clients.Add(new Client
        {
            ClientId = "client",
            ClientSecrets = { new Secret(_securityConfig.Secret.Sha256()) },

            AllowedGrantTypes = GrantTypes.ClientCredentials,
            // scopes that client has access to
            AllowedScopes = { "identity" }
        });

        Clients.Add(new Client
        {
            ClientId = "mvc",
            ClientName = "MVC Client",

            AllowedGrantTypes = GrantTypes.HybridAndClientCredentials,
            //RequirePkce = true,
            ClientSecrets = { new Secret(_securityConfig.Secret.Sha256()) },
            RequireConsent = false,
            RedirectUris = _securityConfig.RedirectURIs,
            FrontChannelLogoutUri = _securityConfig.SignoutUris,
            PostLogoutRedirectUris = _securityConfig.PostLogoutUris,
            AllowOfflineAccess = true,
            AllowAccessTokensViaBrowser = true,
            AllowedScopes = new List<string>
                {
                    IdentityServerConstants.StandardScopes.OpenId,
                    IdentityServerConstants.StandardScopes.Profile,
                    IdentityServerConstants.StandardScopes.Email,
                    IdentityServerConstants.StandardScopes.OfflineAccess,
                    "identity"
                }

        });

        return Clients;
    } 

我的API配置

 services.AddAuthentication("Bearer")
            .AddJwtBearer("Bearer", options =>
            {
                options.Authority = _securityConfig.Authority;
                options.RequireHttpsMetadata = false;

                options.Audience = "identity";
            });

最后是我的Web应用程序,OIDC配置以及如何获取访问令牌:

        services.AddAuthentication(options =>
        {
            options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
            options.DefaultChallengeScheme = "oidc";
        }).AddCookie(options =>
            {
                options.ExpireTimeSpan = TimeSpan.FromMinutes(60);
                options.Cookie.Name = "identity_cookie";
            })
        .AddOpenIdConnect("oidc", options =>
        {
            options.Events = new OpenIdConnectEvents
            {
                OnUserInformationReceived = async ctx =>
                {
                    //Get Token here and assign to Cookie for use in Jquery
                    ctx.HttpContext.Response.Cookies.Append("bearer_config", ctx.ProtocolMessage.AccessToken);
                }
            };

            options.Authority = _securityConfig.Authority;
            options.RequireHttpsMetadata = false;

            options.ClientId = "mvc";
            options.ClientSecret = _securityConfig.Secret;
            options.ResponseType = "code id_token";
            options.SaveTokens = true;


            options.Scope.Clear();
            options.Scope.Add("openid");
            options.Scope.Add("profile");
            options.Scope.Add("email");
            options.Scope.Add("identity");
            options.Scope.Add("offline_access");

            options.ClaimActions.MapAllExcept("iss", "nbf", "exp", "aud", "nonce", "iat", "c_hash");

            options.GetClaimsFromUserInfoEndpoint = true;
            //options.SaveTokens = true;

            options.TokenValidationParameters = new TokenValidationParameters
            {
                NameClaimType = JwtClaimTypes.Name,
                RoleClaimType = JwtClaimTypes.Role,
            };


        });

我一直收到401未经授权的错误,有什么想法吗?


你能验证一下在Startup.Configure中中间件的顺序吗?特别是UseAuthorization语句。 - user4864425
@RuardvanElburg,我的中间件如下:app.UseHttpsRedirection(); app.UseRouting(); app.UseAuthorization(); app.UseAuthentication(); - Jacques Bronkhorst
3
正好相反,先使用UseAuthentication再使用UseAuthorization。首先确定用户身份,然后授权用户。这就是为什么中间件不起作用,但属性起作用的原因。 - user4864425
@RuardvanElburg 它有效了!只需将其添加为答案,我就会接受它。您先生是一位学者和绅士! - Jacques Bronkhorst
这也可能发生在你够愚蠢,复制并粘贴app.UseAuthorization();两次,而不是像我这样使用app.UseAuthentication()。我花了几个小时才意识到这一点。 - GeorgiG
1个回答

7

根据所描述的行为,我认为这可能与中间件配置有关,更具体地说是中间件的顺序。但由于问题中没有提供 Startup.Configure 函数,我无法确定。

幸运的是,Jacques 能够确认问题确实与中间件的顺序有关。正如评论中提到的那样:

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

问题在于,用户首先被授权 (UseAuthorization) 然后再进行身份验证 (UseAuthentication)。因此,在那时用户是未知的 (匿名的),因此无法进行授权。但稍后,在验证属性时,用户变为已知。这就解释了为什么它能够工作。
为了解决这个问题,语句必须倒转。首先对用户进行身份验证 (确定用户是谁?) ,然后授权该用户。
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();

订单的描述在文档中。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接