.NET Core Web API中使用自定义属性进行JWT身份验证

4
我目前正在将我的Web API 2.0转换为.NET Core Web API,但有一个部分让我很困惑。
在我现有的API中,我有一个带有以下代码的属性:
public class JwtAuthentication : Attribute, IAuthenticationFilter
{
    public string Realm { get; set; }

    public bool AllowMultiple => false;

    public async Task AuthenticateAsync(
        HttpAuthenticationContext context, 
        CancellationToken cancellationToken)
    {
        var request = context.Request;

        var authorization = request.Headers.Authorization;

        // checking request header value having required scheme "Bearer" or not.
        if (authorization == null ||
            authorization.Scheme.ToLowerInvariant() != "bearer" ||
            string.IsNullOrEmpty(authorization.Parameter))
        {
            context.ErrorResult = new AuthenticationFailureResult("JWT Token is Missing", request);
            return;
        }

        // Getting Token value from header values.
        var token = authorization.Parameter;
        var principal = await AuthJwtToken(token);

        if (principal == null)
        {
            context.ErrorResult = new AuthenticationFailureResult("Invalid JWT Token", request);
        }
        else
        {
            context.Principal = principal;
        }
    }

    private static bool ValidateToken(string token, out ICollection<Claim> claims)
    {
        claims = null;

        var simplePrinciple = JwtAuthManager.GetPrincipal(token);

        if (simplePrinciple == null)
        {
            return false;
        }

        var identity = simplePrinciple.Identity as ClaimsIdentity;

        if (identity == null)
        {
            return false;
        }

        if (!identity.IsAuthenticated)
        {
            return false;
        }

        var usernameClaim = identity.FindFirst(ClaimTypes.Name);
        var emailClaim = identity.FindFirst(ClaimTypes.Email);

        var username = usernameClaim?.Value;
        var email = emailClaim?.Value;

        if (string.IsNullOrEmpty(username) || string.IsNullOrEmpty(email))
        {
            return false;
        }

        claims = identity.Claims.ToList();

        return true;
    }

    protected Task<IPrincipal> AuthJwtToken(string token)
    {
        if (ValidateToken(token, out var claims))
        {
            var identity = new ClaimsIdentity(claims, "Jwt");

            IPrincipal user = new ClaimsPrincipal(identity);

            return Task.FromResult(user);
        }

        return Task.FromResult<IPrincipal>(null);
    }

    public Task ChallengeAsync(
        HttpAuthenticationChallengeContext context, 
        CancellationToken cancellationToken)
    {
        Challenge(context);
        return Task.FromResult(0);
    }

    private void Challenge(HttpAuthenticationChallengeContext context)
    {
        string parameter = null;

        if (!string.IsNullOrEmpty(Realm))
        {
            parameter = "realm=\"" + Realm + "\"";
        }

        context.ChallengeWith("Bearer", parameter);
    }
}

如果我理解正确,在ASP.NET Core中,我只需要在启动时定义以下内容:
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
    .AddJwtBearer(options =>  
    {  
        options.TokenValidationParameters = new TokenValidationParameters  
        {  
            ValidateIssuer = true,  
            ValidateAudience = true,  
            ValidateLifetime = true,  
            ValidateIssuerSigningKey = true,  
            ValidIssuer = Configuration["Jwt:Issuer"],  
            ValidAudience = Configuration["Jwt:Issuer"],  
            IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["Jwt:Key"]))  
        };  
    });

"我不确定我是否需要以下内容,但它看起来像是需要的:"
services.AddMvc(); 

“所有我能做的就是使用[Authorize]属性,但如果我想复制我在ASP.NET MVC Web API 2.0中使用的属性怎么办?”
“我应该这样做吗?我喜欢它可以让我看到令牌出了什么问题。如果它可以以同样的方式使用,并且假设这样做是可以的,那么我该如何做呢?我在谷歌上搜索解决方案时没有找到任何有用的东西。”
“谢谢。”
2个回答

1

根据@dropoutcoder的答案,由于options.Events中的Events为null,我遇到了一个object reference not set...的错误。为了解决这个问题,我使用了以下代码:

options.Events = new JwtBearerEvents()
{
    OnMessageReceived = context =>
    {
        return Task.CompletedTask;
    },
    OnAuthenticationFailed = context =>
    {
        return Task.CompletedTask;
    },
    OnTokenValidated = context =>
    {
        return Task.CompletedTask;
    },
    OnChallenge = context =>
    {
        return Task.CompletedTask;
    },
    OnForbidden = context =>
    {
        return Task.CompletedTask;
    }
};

0

我猜你不想重新发明整个令牌认证的轮子。

如果您希望自定义事件的处理方式,可以使用JwtBearerOptions.Events Property将您自己的委托挂钩到其中一个或多个事件上(OnAuthenticationFailed Property, OnChallenge Property, OnMessageReceived Property, OnTokenValidated Property)。

示例失败身份验证日志记录。

services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
    .AddJwtBearer(options =>  
    {  
        options.TokenValidationParameters = new TokenValidationParameters  
        {  
            ValidateIssuer = true,  
            ValidateAudience = true,  
            ValidateLifetime = true,  
            ValidateIssuerSigningKey = true,  
            ValidIssuer = Configuration["Jwt:Issuer"],  
            ValidAudience = Configuration["Jwt:Issuer"],  
            IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["Jwt:Key"]))  
        };

    options.Events.OnAuthenticationFailed = (context) =>
    {
        // Log failed authentication here

        // Return control back to JWT Bearer middleware
        return Task.CompletedTask;
    }
});

希望它有所帮助


对延迟感到抱歉。这些事件正是我在寻找的。我会马上检查其他的,但这绝对是一个良好的开端。再次感谢。 - Thierry
实际上我说得太快了。我遇到了一个错误 System.NullReferenceException: 'Object reference not set to an instance of an object.',在进一步阅读 Microsoft 网站上的内容后,似乎只兼容 .net core 2.2 版本,除非他们的文档已过时。稍后会更新。 - Thierry

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