[英].NET Core 2.0 -> 5.0 API Authentication- Multiple Schemes
I have a .NET Core (Was 2.0, now step by step upgraded to 5) web application using MVC and standard Identity.我有一个 .NET 核心(是 2.0,现在逐步升级到 5) web 使用 MVC 和标准身份的应用程序。 It has a web based login/backend UI.
它有一个基于 web 的登录/后端 UI。 The upgrade process has worked fine for this and all is operating as it should.
升级过程对此运行良好,并且一切正常运行。
However, I also have a set of WebAPI controllers which I have using a JWT Bearer Token - and these have stopped working, and now all throw a 401 error.但是,我还有一组 WebAPI 控制器,我使用 JWT Bearer Token - 它们已经停止工作,现在都抛出 401 错误。
I am pretty sure I need to somehow register the additional authorization scheme, but I am not sure how to do it.我很确定我需要以某种方式注册额外的授权方案,但我不知道该怎么做。
Here is how the controller is annotation这是 controller 的注释方式
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
[Route("api/[controller]")]
And here is the excerpt from my Startup.cs这是我的 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?}");
});
}
}
It might be better if you understand the cause of the error, so i'll explain a bit here.如果你了解错误的原因可能会更好,所以我会在这里解释一下。
We all use the app.UseAuthentication();
我们都使用
app.UseAuthentication();
in out startup class, which behind the scenes process like this . in out startup class,幕后过程是这样的。
In your above configuration, you provide the middleware 2 schemes to process在你上面的配置中,你提供了中间件 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,
};
});
The first was CookieAuthenticationDefaults.AuthenticationScheme
and the second one is JwtBearerDefaults.AuthenticationScheme
.第一个是
CookieAuthenticationDefaults.AuthenticationScheme
,第二个是JwtBearerDefaults.AuthenticationScheme
。 Then they both got execute(I know you point out the scheme, but you don't set the default scheme, so Schemes.GetDefaultAuthenticateSchemeAsync()
will return something probably not what we want).然后他们都执行了(我知道你指出了方案,但你没有设置默认方案,所以
Schemes.GetDefaultAuthenticateSchemeAsync()
将返回一些可能不是我们想要的东西)。
Solution: Use a default scheme, and put some logic to forward the pipeline the the right processor!解决方案:使用默认方案,并放置一些逻辑将管道转发给正确的处理器!
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);
Done, give it a shot完成,试一试
[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: I currently using this too. P/s: 我目前也在用这个。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.