在.net core中使用身份验证方案时返回403(forbidden)

Tej*_*jas 6 c# authentication jwt .net-core

我正在使用JWT安全令牌和自定义身份验证方案在我的 Web 应用程序中进行身份验证。

  1. 我在用户登录时生成令牌

  2. 我创建了一个身份验证处理程序,在其中验证所有请求的令牌

身份验证处理程序

public class CustomAuthenticationHandler : AuthenticationHandler<CustomAuthenticationOptions>
{
    public CustomAuthenticationHandler(
        IOptionsMonitor<CustomAuthenticationOptions> options,
        ILoggerFactory logger, 
        UrlEncoder encoder, 
        ISystemClock clock)
    : base(options, logger, encoder, clock)
    {

    }

    protected override Task<AuthenticateResult> HandleAuthenticateAsync()
    {
        try
        {
            Exception ex;
            var key = Request.Headers[Options.HeaderName].First();

            if (!IsValid(key, out ex))
            {

                return Task.FromResult(AuthenticateResult.Fail(ex.Message));
                //filterContext.Result = new CustomUnauthorizedResult(ex.Message);
            }
            else
            {
              
                AuthenticationTicket ticket = new AuthenticationTicket(new ClaimsPrincipal(),new AuthenticationProperties(),this.Scheme.Name);
                return Task.FromResult(AuthenticateResult.Success(ticket));
            }
        }
        catch (InvalidOperationException)
        {
            return Task.FromResult(AuthenticateResult.Fail(""));
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

自定义身份验证扩展

public static class CustomAuthenticationExtensions
{
    public static AuthenticationBuilder AddCustomAuthentication(this AuthenticationBuilder builder, string authenticationScheme, string displayName, Action<CustomAuthenticationOptions> configureOptions)
    {
        return builder.AddScheme<CustomAuthenticationOptions, CustomAuthenticationHandler>(authenticationScheme, displayName, configureOptions);
    }
}
Run Code Online (Sandbox Code Playgroud)

将自定义身份验证集成到启动中

public class Startup
{
    public Startup(IConfiguration configuration)
    {
        Configuration = configuration;
    }

    public IConfiguration Configuration { get; }

    // This method gets called by the runtime. Use this method to add services to the container.
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddDbContext<ApplicationDbContext>(options => options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
        services.AddMvc().AddFluentValidation(fv => fv.RegisterValidatorsFromAssemblyContaining<Person>());
        services.AddTransient<IRepositoryWrapper, RepositoryWrapper>();
        services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);

        services.AddAuthentication(options=> {
            options.DefaultScheme = "CustomScheme";
            options.DefaultAuthenticateScheme = "CustomScheme";
            options.DefaultChallengeScheme = "CustomScheme";
        }).AddCustomAuthentication("CustomScheme", "CustomScheme", o => { });

    }


    public void Configure(IApplicationBuilder app, IHostingEnvironment env,ILoggerFactory loggerFactory)
    {
        loggerFactory.AddConsole();
        loggerFactory.AddDebug(LogLevel.Information);

        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }
        else
        {
            app.UseHsts();
        }

        app.UseAuthentication();
        app.UseHttpsRedirection();
        app.UseMvc();

    }
}
Run Code Online (Sandbox Code Playgroud)

在控制器中使用身份验证方案

    [Authorize(AuthenticationSchemes ="CustomScheme")]
    [ApiController]
    [Route("api/controller")]
    public class UserController : BaseController
    {
        
        public UserController(IRepositoryWrapper repository) : base(repository)
        {
        }
        
        [HttpGet]
        public IEnumerable<Users> Get()
        {
            return _repository.Users.FindAll();
        }
    }
Run Code Online (Sandbox Code Playgroud)

当我使用有效令牌从 Postman 调用 API 时,它会返回错误403

请帮忙解决这个问题...!!

小智 20

对于遇到此问题的其他人:

最初的问题似乎是在 AuthenticationHandler 中返回 AuthenticationResult 时,ClaimsIdentity 未传递给 ClaimsPrincipal。另请注意,身份验证类型必须传递给 ClaimsIdentity,否则 IsAuthenticated 将为 false。在 AuthenticationHandler 中执行类似的操作应该可以解决该问题:

else
{
    var claimsPrincipal = new ClaimsPrincipal();
    var claimsIdentity = new ClaimsIdentity("JWT");
    claimsPrincipal.AddIdentity(claimsIdentity);

    var ticket = new AuthenticationTicket(claimsPrincipal, this.Scheme.Name);

    return AuthenticateResult.Success(ticket);
}
Run Code Online (Sandbox Code Playgroud)

  • 就我而言,我缺少 ClaimsIdentity 的参数 `authenticationType`。显然,你可以写所有的东西,但如果不指定它就不起作用。 (2认同)

Tej*_*jas 1

我找到了解决方案。

我用CustomAuthenticationMiddleclass代替AuthenticationHandlerclass

public class CustomAuthenticationMiddleware
{
    private readonly RequestDelegate _next;
    public CustomAuthenticationMiddleware(RequestDelegate next)
    {
        _next = next;
    }
    public async Task Invoke(HttpContext context)
    {
            try
            {
                Exception ex;
                var key = context.Request.Headers["Authorization"].First();

                if (!IsValid(key))
                {
                       //logic for authentication
                }
                else
                {

                    await _next.Invoke(context);
                }
            }
            catch (InvalidOperationException)
            {
                context.Response.StatusCode = 401; //Unauthorized
                return;
            }
        }

    private bool IsValid(string key)
    {
        //Code for checking the key is valid or not
    }
}
Run Code Online (Sandbox Code Playgroud)

将上面用于集成自定义身份验证处理程序的所有代码替换为类下的下一Startup

app.UseMiddleware<CustomAuthenticationMiddleware>();
Run Code Online (Sandbox Code Playgroud)