【问题标题】:Issue with custom Authentication filter asp.net core自定义身份验证过滤器 asp.net core 的问题
【发布时间】:2020-10-24 13:18:36
【问题描述】:

我正在尝试在 ASP.NET Core 中创建自定义身份验证过滤器。需要在控制器中使用它来验证提供给我的 JWT 并创建一个 Claim Principal。但是,当我将身份验证标签放在控制器上方时,没有任何反应,并且控制器在没有身份验证的情况下得到处理。

以下是已完成的步骤:

  1. 在startup.cs下添加了app.UseAuthentication()
    Configure(IApplicationBuilder app, IHostingEnvironment env)
     {
        ......
        ......
        app.UseAuthentication();
     }
  1. 在同一个项目中创建了一个新的类文件 ProcessAuth,包含 AuthenticationAsync 和 ChallengeAsync
   public class ProcessAuth : Attribute, IAuthenticationFilter
    {
        public bool AllowMultiple { get { return false; } }


        public async Task AuthenticateAsync(HttpAuthenticationContext context, CancellationToken cancellationToken)
        {            
            HttpRequestMessage request = context.Request;
            AuthenticationHeaderValue authorization = request.Headers.Authorization;
            
            // More code to be added for validating the JWT
        }

        public Task ChallengeAsync(HttpAuthenticationChallengeContext context, CancellationToken cancellationToken)
        {
            throw new NotImplementedException(); //sample code
        }
    } 
  1. 在控制器中添加了这个新文件的引用
  2. 将标签 [ProcessAuth] 放在控制器顶部
    [ProcessAuth]
    [Route("api/[controller]")]
    [ApiController] 
  1. 使用 Postman 发送 JSON 数据以及包含有效 JWT 令牌作为“Bearer”的授权标头
  2. 代码只是忽略过滤器,处理控制器中的代码并返回结果

更多信息: 如果我将[Authorize] 添加到控制器,邮递员只会返回一个401 Unauthorized error

还检查了this URL,但找不到问题。

更新:我检查了类似 Stack Overflow 问题的答案并遵循相同的方法,但问题仍然存在。

已安装 Nuget 包: Microsoft.AspNet.WebApi.Core 和 Microsoft.AspNet.WebApi

使用的命名空间:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Security.Claims;
using System.Threading;
using System.Threading.Tasks;
using System.Web.Http.Filters;
using System.Web.Http.Controllers;

Similar Issue - Link

如何让它工作?我错过了什么吗?

【问题讨论】:

    标签: authentication asp.net-core-webapi jwt-auth


    【解决方案1】:

    根据您的代码,我发现您在 asp.net 核心应用程序中使用了 asp.net 身份验证过滤器。这是行不通的。

    在asp.net core中,我们应该使用JWT熊认证中间件来实现你的要求。

    您可以创建自定义 OnChallenge 来验证 jwt 令牌并创建 OnTokenValidated 来添加声明。

    更多细节,您可以参考以下代码:

            services.AddAuthentication(auth =>
            {
                auth.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
                auth.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
            })
            .AddJwtBearer(token =>
            {
                token.RequireHttpsMetadata = false;
                token.SaveToken = true;
                token.TokenValidationParameters = new TokenValidationParameters
                {
                    ValidateIssuerSigningKey = true,
                    //Same Secret key will be used while creating the token
                    IssuerSigningKey = new SymmetricSecurityKey(SecretKey),
                    ValidateIssuer = true,
                    //Usually, this is your application base URL
                    ValidIssuer = "http://localhost:45092/",
                    ValidateAudience = true,
                    //Here, we are creating and using JWT within the same application.
                    //In this case, base URL is fine.
                    //If the JWT is created using a web service, then this would be the consumer URL.
                    ValidAudience = "http://localhost:45092/",
                    RequireExpirationTime = true,
                    ValidateLifetime = true,
                    ClockSkew = TimeSpan.Zero
                };
    
                token.Events = new JwtBearerEvents { 
                    
                     OnChallenge = async ctx => { 
                     
                     },
                      OnTokenValidated = async ctx =>
                      {
                          //Get the calling app client id that came from the token produced by Azure AD
                          string clientId = ctx.Principal.FindFirstValue("appid");
    
                          //Get EF context
                          //var db = ctx.HttpContext.RequestServices.GetRequiredService<AuthorizationDbContext>();
    
                          //Check if this app can read confidential items
                          bool canReadConfidentialItems = await db.Applications.AnyAsync(a => a.ClientId == clientId && a.ReadConfidentialItems);
                          if (canReadConfidentialItems)
                          {
                              //Add claim if yes
                              var claims = new List<Claim>
                    {
                        new Claim("ConfidentialAccess", "true")
                    };
                              var appIdentity = new ClaimsIdentity(claims);
    
                              ctx.Principal.AddIdentity(appIdentity);
                          }
                      }
                };
            });
    

    编辑:

    您可以创建如下所示的 AuthenticationHandler 和 AuthenticationSchemeOptions 类,并在 startup.cs 中注册该类。然后你可以使用[Authorize(AuthenticationSchemes = "Test")] 来设置特殊的AuthenticationSchemes。

    更多细节,您可以参考以下代码示例:

    public class ValidateHashAuthenticationSchemeOptions : AuthenticationSchemeOptions
    {
    
    }
    
    public class ValidateHashAuthenticationHandler
    : AuthenticationHandler<ValidateHashAuthenticationSchemeOptions>
    {
        public ValidateHashAuthenticationHandler(
            IOptionsMonitor<ValidateHashAuthenticationSchemeOptions> options,
            ILoggerFactory logger,
            UrlEncoder encoder,
            ISystemClock clock)
            : base(options, logger, encoder, clock)
        {
        }
    
        protected override Task<AuthenticateResult> HandleAuthenticateAsync()
        {
            //TokenModel model;
    
            // validation comes in here
            if (!Request.Headers.ContainsKey("X-Base-Token"))
            {
                return Task.FromResult(AuthenticateResult.Fail("Header Not Found."));
            }
    
            var token = Request.Headers["X-Base-Token"].ToString();
    
            try
            {
                // convert the input string into byte stream
                using (MemoryStream stream = new MemoryStream(Encoding.UTF8.GetBytes(token)))
                {
                    // deserialize stream into token model object
                    //model = Serializer.Deserialize<TokenModel>(stream);
                }
            }
            catch (System.Exception ex)
            {
                Console.WriteLine("Exception Occured while Deserializing: " + ex);
                return Task.FromResult(AuthenticateResult.Fail("TokenParseException"));
            }
    
            //if (model != null)
            //{
            //    // success case AuthenticationTicket generation
            //    // happens from here
    
            //    // create claims array from the model
            //    var claims = new[] {
            //        new Claim(ClaimTypes.NameIdentifier, model.UserId.ToString()),
            //        new Claim(ClaimTypes.Email, model.EmailAddress),
            //        new Claim(ClaimTypes.Name, model.Name) };
    
            //    // generate claimsIdentity on the name of the class
            //    var claimsIdentity = new ClaimsIdentity(claims,
            //                nameof(ValidateHashAuthenticationHandler));
    
            //    // generate AuthenticationTicket from the Identity
            //    // and current authentication scheme
            //    var ticket = new AuthenticationTicket(
            //        new ClaimsPrincipal(claimsIdentity), this.Scheme.Name);
    
            //    // pass on the ticket to the middleware
            //    return Task.FromResult(AuthenticateResult.Success(ticket));
            //}
    
            return Task.FromResult(AuthenticateResult.Fail("Model is Empty"));
        }
    
    }
    public class TokenModel
    {
        public int UserId { get; set; }
        public string Name { get; set; }
        public string EmailAddress { get; set; }
    }
    

    Startup.cs 将以下代码添加到 ConfigureServices 方法中:

                services.AddAuthentication(options =>
                {
                    options.DefaultScheme
                        = "Test";
                })
    .AddScheme<ValidateHashAuthenticationSchemeOptions, ValidateHashAuthenticationHandler>
            ("Test", null);
    

    控制器:

    [Authorize(AuthenticationSchemes = "Test")]
    

    【讨论】:

    • 非常感谢您的回复。 JWT 验证可以在单独的类文件而不是启动文件中完成吗?我还使用公共证书来验证令牌,并且给我的这个令牌是使用 RS256 算法签名的。既然是非对称密钥,那么如何从证书中获取公钥呢?
    • 您可以像下面这样创建 AuthenticationHandler 和 AuthenticationSchemeOptions 类,并在 startup.cs 中注册该类。请参阅我的更新答案。
    • 感谢 Brando,我能够使用 AuthenticationHandler 和 AuthenticationSchemeOptions 让它工作。需要认证的特定控制器也带有 DefaultScheme 的名称。
    • 现在,有一个新的挑战。对于整个站点,我被要求实现 JwtBearer 身份验证并仅将 AuthenticationHandler 和 AuthenticationSchemeOptions 类用于一个控制器。可以同时使用吗?
    • 是的,我们可以在 asp.net core 中定义多个 AuthenticationScheme。请参考此article
    猜你喜欢
    • 2017-02-15
    • 2018-02-24
    • 2016-07-05
    • 2014-03-03
    • 2018-03-15
    • 1970-01-01
    • 2016-12-11
    相关资源
    最近更新 更多