asp.net core5.0 webapi项目自定义AuthenticationHandler鉴权报错IDW10203,如何解决?

我使用了自定义的AuthenticationHandler,当请求localhost:5011/WeatherForecast?_token=123456,总是报错 "IDW10203: The 'scope' or 'scp' claim does not contain scopes 'access_as_user' or was not found" ,为什么会报这个错误,该如何解决它呢?
我是想通过DemoAuthenticationHandler来实现自定义简单的身份验证,就是传入一个发给使用者不公开的_token字符串(不是一个规范的token更像一个加密的密码),验证通过了就可以访问。我使用asp.net core5.0的提供的Identity相关的package如何实现我的这个需求呢

public class DemoAuthenticationOptions : AuthenticationSchemeOptions
   public const string Scheme = "Demo";
}

public class DemoAuthenticationHandler : AuthenticationHandler<DemoAuthenticationOptions>
{

    public DemoAuthenticationHandler(IOptionsMonitor<DemoAuthenticationOptions> options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock)

    : base(options, logger, encoder, clock)
    { }

    protected override Task<AuthenticateResult> HandleAuthenticateAsync()
    {

        if (!Request.Query.TryGetValue("_token", out var keys))
        {
            return Task.FromResult(AuthenticateResult.Fail("The authentication process failed."));
        }

        var key = keys.FirstOrDefault();

        if (key == "123456")
        {
            var claims = new List<Claim>
                {
                    new Claim(ClaimTypes.Name, "My IO")
                };

            var identity = new ClaimsIdentity(claims, DemoAuthenticationOptions.Scheme);

            var identities = new List<ClaimsIdentity> { identity };

            var principal = new ClaimsPrincipal(identities);

            var ticket = new AuthenticationTicket(principal, DemoAuthenticationOptions.Scheme);

            return Task.FromResult(AuthenticateResult.Success(ticket));

        }

        return Task.FromResult(AuthenticateResult.Fail("The authentication process failed."));

    }
}

public static class AuthenticationBuilderExtensions
{

    public static AuthenticationBuilder AddDemoAuthentication(this AuthenticationBuilder authenticationBuilder, Action<DemoAuthenticationOptions> options)
    {
        return authenticationBuilder.AddScheme<DemoAuthenticationOptions, DemoAuthenticationHandler>(DemoAuthenticationOptions.Scheme, options);
    }
}

public class Startup
{

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

    public IConfiguration Configuration { get; }

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddControllers();
        
        services.AddAuthentication(option =>
        {
            option.DefaultAuthenticateScheme = DemoAuthenticationOptions.Scheme;
            option.DefaultChallengeScheme = DemoAuthenticationOptions.Scheme;
        })
        .AddDemoAuthentication(options => { });
    }

    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }

        app.UseHttpsRedirection();

        app.UseRouting();

        app.UseAuthentication();

        app.UseAuthorization();

        app.UseEndpoints(endpoints =>
        {
            endpoints.MapControllers().RequireAuthorization(DemoAuthenticationOptions.Scheme);
        });
    }
}

原来是我自己搞错了,在action里面有一句验证scopes的代码


using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.IdentityModel.Tokens;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Text;
using System.Threading.Tasks;

namespace YourNamespace
{
    public class DemoAuthenticationOptions : AuthenticationSchemeOptions
    {
        public const string Scheme = "Demo";
    }

    public class DemoAuthenticationHandler : AuthenticationHandler<DemoAuthenticationOptions>
    {
        public DemoAuthenticationHandler(IOptionsMonitor<DemoAuthenticationOptions> options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock)
            : base(options, logger, encoder, clock)
        { }

        protected override Task<AuthenticateResult> HandleAuthenticateAsync()
        {
            if (!Request.Query.TryGetValue("_token", out var keys))
            {
                return Task.FromResult(AuthenticateResult.Fail("The authentication process failed."));
            }

            var key = keys.FirstOrDefault();

            if (key == "123456")
            {
                var claims = new List<Claim>
                {
                    new Claim(ClaimTypes.Name, "My IO"),
                    new Claim("scope", "access_as_user")
                };

                var identity = new ClaimsIdentity(claims, DemoAuthenticationOptions.Scheme);

                var identities = new List<ClaimsIdentity> { identity };

                var principal = new ClaimsPrincipal(identities);

                var ticket = new AuthenticationTicket(principal, DemoAuthenticationOptions.Scheme);

                return Task.FromResult(AuthenticateResult.Success(ticket));
            }

            return Task.FromResult(AuthenticateResult.Fail("The authentication process failed."));
        }
    }

    public static class AuthenticationBuilderExtensions
    {
        public static AuthenticationBuilder AddDemoAuthentication(this AuthenticationBuilder authenticationBuilder, Action<DemoAuthenticationOptions> options)
        {
            return authenticationBuilder.AddScheme<DemoAuthenticationOptions, DemoAuthenticationHandler>(DemoAuthenticationOptions.Scheme, options);
        }
    }

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

        public IConfiguration Configuration { get; }

        public void ConfigureServices(IServiceCollection services)
        {
            services.AddControllers();

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

            services.AddAuthorization();

            services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
                .AddJwtBearer(options =>
                {
                    options.TokenValidationParameters = new TokenValidationParameters
                    {
                        ValidateIssuer = true,
                        ValidateAudience = true,
                        ValidateLifetime = true,
                        ValidateIssuerSigningKey = true,
                        ValidIssuer = "your-issuer",
                        ValidAudience = "your-audience",
                        IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("your-secret-key"))
                    };
                });
        }

        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }

            app.UseHttpsRedirection();

            app.UseRouting();

            app.UseAuthentication();

            app.UseAuthorization();

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllers().RequireAuthorization();
            });
        }
    }
}

这个错误提示是因为你的 AuthenticationHandler 在处理请求时,需要检查请求中是否包含了指定的 scope(作用域),但是在请求中并没有包含该 scope 或者根本没有包含任何 scope,因此导致了该错误。

在使用自定义的 AuthenticationHandler 时,你需要通过实现 HandleAuthenticateAsync 方法来处理请求的身份验证。其中,你可以通过以下代码来获取请求中包含的 scope:

Copy
var scopes = principal.FindFirst("scope")?.Value?.Split(' ');
在这里,principal 是一个 ClaimsPrincipal 对象,它包含了当前请求的身份验证信息。通过 FindFirst 方法获取到了名为 "scope" 的 claim,之后使用 Value 属性获取到了 claim 的值,并使用 Split 方法将其分割成一个字符串数组。在这个数组中,每个元素表示一个 scope。

接下来,你需要检查这个 scope 数组中是否包含了你需要的 scope。比如,如果你需要检查是否包含了 "access_as_user" 这个 scope,可以使用以下代码:

if (scopes == null || !scopes.Contains("access_as_user"))
{
    return AuthenticateResult.Fail("The 'access_as_user' scope is required.");
}

这段代码首先检查 scope 数组是否为空,如果为空则表示请求中没有包含任何 scope,因此直接返回身份验证失败。否则,它会使用 Contains 方法检查数组中是否包含了 "access_as_user" 这个 scope。如果没有包含,则认为身份验证失败,并返回错误信息。

因此,如果你在请求中传递的 _token 参数并没有包含 "access_as_user" 这个 scope,就会出现该错误。你需要确保请求中包含了指定的 scope,或者修改你的代码,使其不要要求指定的 scope。
回答整理自chatgpt,如果有帮助麻烦采纳一下,谢谢啦!