繁体   English   中英

.NET 核心 2.0 -> 5.0 API 身份验证 - 多种方案

[英].NET Core 2.0 -> 5.0 API Authentication- Multiple Schemes

我有一个 .NET 核心(是 2.0,现在逐步升级到 5) web 使用 MVC 和标准身份的应用程序。 它有一个基于 web 的登录/后端 UI。 升级过程对此运行良好,并且一切正常运行。

但是,我还有一组 WebAPI 控制器,我使用 JWT Bearer Token - 它们已经停止工作,现在都抛出 401 错误。

我很确定我需要以某种方式注册额外的授权方案,但我不知道该怎么做。

这是 controller 的注释方式

    [Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
    [Route("api/[controller]")]

这是我的 Startup.cs 的摘录

public void ConfigureServices(IServiceCollection services)
        {
            services.AddDbContext<ApplicationDbContext>(options =>
                options.UseSqlServer(Configuration.GetConnectionString("TechsportiseDB")));
            //options.UseInMemoryDatabase("Techsportise"));

            services.AddIdentity<ApplicationUser, IdentityRole>(config =>
                {
                    config.SignIn.RequireConfirmedEmail = true;
                })
                .AddEntityFrameworkStores<ApplicationDbContext>()
                .AddDefaultTokenProviders();

            services.Configure<IdentityOptions>(options =>
            {
                // Omitted
            });

            services.ConfigureApplicationCookie(options =>
            {
                // Cookie settings
                options.Cookie.HttpOnly = true;
                options.ExpireTimeSpan = TimeSpan.FromMinutes(30);
                options.SlidingExpiration = true;
            });

           
            services.AddSwaggerGen(c =>
            {
                c.SwaggerDoc("v1", new OpenApiInfo { Title = "Techsportise API", Version = "v1" });
                c.OperationFilter<AddRequiredHeaderParameter>();
                var filePath = Path.Combine(PlatformServices.Default.Application.ApplicationBasePath, "TechsportiseOnline.xml");
                c.IncludeXmlComments(filePath);
            });

            services.Configure<JWTSettings>(Configuration.GetSection("JWTSettings"));

            services.AddAuthentication()
                .AddCookie()
                .AddJwtBearer(options =>
                {
                    options.RequireHttpsMetadata = false;
                    options.IncludeErrorDetails = true;

                    var secretKey = Configuration.GetSection("JWTSettings:SecretKey").Value;
                    var signingKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(secretKey));

                    options.TokenValidationParameters = new TokenValidationParameters
                    {

                        ValidateIssuer = true,
                        ValidIssuer = Configuration.GetSection("JWTSettings:Issuer").Value,
                        ValidateAudience = true,
                        ValidAudience = Configuration.GetSection("JWTSettings:Audience").Value,
                        ValidateIssuerSigningKey = true,
                        IssuerSigningKey = signingKey,

                    };
                    
                });


            services.AddAuthorization();
           

            services.AddMvcCore(option => option.EnableEndpointRouting = false)
                .AddViewLocalization(
                    LanguageViewLocationExpanderFormat.Suffix,
                    opts => { opts.ResourcesPath = "Resources"; })
                .AddDataAnnotationsLocalization()
                .AddApiExplorer();


            services.AddAntiforgery();

            var skipSSL = Configuration.GetValue<bool>("LocalTest:skipSSL");

            // requires using Microsoft.AspNetCore.Mvc;
            services.Configure<MvcOptions>(options =>
            {
                // Set LocalTest:skipSSL to true to skip SSL requrement in 
                // debug mode. This is useful when not using Visual Studio.
                if (!skipSSL)
                {
                    options.Filters.Add(new RequireHttpsAttribute());
                }
            });

            services.AddSingleton<SharedViewLocalizer>();
            services.AddSingleton<SharedViewHtmlLocalizer>();

            services.AddControllersWithViews();

            
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {

            if (env.EnvironmentName == "Development")
            {
                app.UseDeveloperExceptionPage();
            }
            else
            {
                app.UseExceptionHandler("/Home/Error");
            }

            // Enable middleware to serve generated Swagger as a JSON endpoint.
            app.UseSwagger();

            // Enable middleware to serve swagger-ui (HTML, JS, CSS etc.), specifying the Swagger JSON endpoint.
            app.UseSwaggerUI(c =>
            {
                c.SwaggerEndpoint("/swagger/v1/swagger.json", "Techsportise API V1");
            });


            app.UseDefaultFiles();
            app.UseStaticFiles();

          
            app.UseAuthentication();
            app.UseAuthorization();

            app.UseMvc(routes =>
            {
                routes.MapRoute(
                    name: "default",
                    template: "{controller=Home}/{action=Index}/{id?}");
            });
        }
    }

如果你了解错误的原因可能会更好,所以我会在这里解释一下。

我们都使用app.UseAuthentication(); in out startup class,幕后过程是这样的。

在你上面的配置中,你提供了中间件 2 方案来处理

services.AddAuthentication()
                .AddCookie()
                .AddJwtBearer(options =>
                {
                    options.RequireHttpsMetadata = false;
                    options.IncludeErrorDetails = true;

                    var secretKey = Configuration.GetSection("JWTSettings:SecretKey").Value;
                    var signingKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(secretKey));

                    options.TokenValidationParameters = new TokenValidationParameters
                    {

                        ValidateIssuer = true,
                        ValidIssuer = Configuration.GetSection("JWTSettings:Issuer").Value,
                        ValidateAudience = true,
                        ValidAudience = Configuration.GetSection("JWTSettings:Audience").Value,
                        ValidateIssuerSigningKey = true,
                        IssuerSigningKey = signingKey,

                    };
                    
                });

第一个是CookieAuthenticationDefaults.AuthenticationScheme ,第二个是JwtBearerDefaults.AuthenticationScheme 然后他们都执行了(我知道你指出了方案,但你没有设置默认方案,所以Schemes.GetDefaultAuthenticateSchemeAsync()将返回一些可能不是我们想要的东西)。

解决方案:使用默认方案,并放置一些逻辑将管道转发给正确的处理器!

services.AddAuthentication(opts =>
                {
                    opts.DefaultScheme = "multi-scheme-election";
                    opts.DefaultChallengeScheme = "multi-scheme-election";
                })
                .AddCookie()
                .AddJwtBearer(options =>
                {
                    options.RequireHttpsMetadata = false;
                    options.IncludeErrorDetails = true;

                    var secretKey = configuration.GetSection("JWTSettings:SecretKey").Value;
                    var signingKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(secretKey));

                    options.TokenValidationParameters = new TokenValidationParameters
                    {

                        ValidateIssuer = true,
                        ValidIssuer = configuration.GetSection("JWTSettings:Issuer").Value,
                        ValidateAudience = true,
                        ValidAudience = configuration.GetSection("JWTSettings:Audience").Value,
                        ValidateIssuerSigningKey = true,
                        IssuerSigningKey = signingKey
                    };
                })
                .AddPolicyScheme("multi-scheme-election", "Your Election scheme processor here",
                    cfgOpts => cfgOpts.ForwardDefaultSelector = ctx =>
                        ctx.Request.Headers.ContainsKey("Authorization")
                            ? JwtBearerDefaults.AuthenticationScheme
                            : CookieAuthenticationDefaults.AuthenticationScheme);

完成,试一试

[HttpGet("TestAuthentication")]
[Authorize] // No need to specify the scheme here, the default policy will take us the right one
public IActionResult TestAuthentication()
{
    return Ok("Authenticated!");
}

P/s: 我目前也在用这个。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM