asp.net-core - 如何配置 dotnetcore 3 站点以使用自定义授权过滤器返回 401,而不是重定向到 AccessDenied 页面
问题描述
我有一个使用新的 dotnetcore3 角度模板的网站。我创建了一个当前非常简单的自定义授权过滤器
public class ClaimRequirementFilter : IAuthorizationFilter
{
readonly string _claim;
public ClaimRequirementFilter(string claim)
{
_claim = claim;
}
public void OnAuthorization(AuthorizationFilterContext context)
{
if (_claim != "test")
{
context.Result = new ForbidResult();
}
}
}
public class ClaimRequirementAttribute : TypeFilterAttribute
{
public ClaimRequirementAttribute(string claimType) : base(typeof(ClaimRequirementFilter))
{
Arguments = new object[] {claimType };
}
}
[Route("{jobId}")]
[ClaimRequirement("testfail")]
[HttpGet]
public async Task<IActionResult> GetJob([FromRoute] Guid jobId)
{
//stuff
{
但是,每当请求失败时(现在将是所有请求),它会将我 302 发送到 AccessDenied 页面,并返回我试图点击的 URL 的 returnUrl。
但是,由于这个请求是从我的角度客户端发出的,我宁愿它只返回一个 401(或 403,因为在这种情况下,这是因为登录用户无权做他们想做的事情),并且我不确定如何配置它。
根据 Ruard 的要求,这是我的启动配置
public class Startup
{
public Startup(IWebHostEnvironment env, IConfiguration configuration)
{
var builder = new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath)
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
.AddEnvironmentVariables();
Configuration = builder.Build();
}
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.AddApplicationInsightsTelemetry();
services.AddDbContext<ApplicationDbContext>(
options => options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"))
);
services.AddTransient<EmailSender, EmailSender>();
services.AddScoped<IRazorViewToStringRenderer, RazorViewToStringRenderer>();
services.Configure<EmailServiceConfiguration>(Configuration.GetSection("EmailServiceConfiguration"));
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
services.AddIdentity<ApplicationUser, IdentityRole>()
.AddDefaultTokenProviders()
.AddEntityFrameworkStores<ApplicationDbContext>();
services.AddIdentityServer(options =>
{
options.UserInteraction.LoginUrl = "/auth/login";
options.UserInteraction.LogoutUrl = "/auth/logout";
})
//.AddDeveloperSigningCredential()
.AddApiAuthorization<ApplicationUser, ApplicationDbContext>();
services.AddAuthentication()
// .AddGoogle(options =>
// {
// IConfigurationSection googleAuthNSection = Configuration.GetSection("Authentication:Google");
// options.ClientId = googleAuthNSection["ClientId"];
// options.ClientSecret = googleAuthNSection["ClientSecret"];
// })
.AddIdentityServerJwt();
services.AddControllersWithViews().AddRazorRuntimeCompilation();
services.AddRazorPages(options =>
{
options.Conventions.AddAreaPageRoute("Identity", "/Identity/Account/Login", "/auth/login");
});
services.AddAuthorization(options =>
{
// options.AddPolicy("RequireAdmin", policy =>
// {
// policy.RequireRole("Admin");
// });
// options.AddPolicy("CreateInternalUsers", policy =>
// {
// // policy.RequireRole("Admin");
// policy.RequireClaim("CreatePPGUser");
// });
});
// In production, the Angular files will be served from this directory
services.AddSpaStaticFiles(configuration =>
{
configuration.RootPath = "ClientApp/dist";
});
services.AddControllers()
.AddNewtonsoftJson(options =>
{
options.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;
});
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ApplicationDbContext context, IServiceProvider services)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseDatabaseErrorPage();
}
else
{
app.UseExceptionHandler("/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
context.Database.Migrate();
app.UseHttpsRedirection();
app.UseStaticFiles();
if (!env.IsDevelopment())
{
app.UseSpaStaticFiles();
}
app.UseRouting();
app.UseAuthentication();
app.UseIdentityServer();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller}/{action=Index}/{id?}"
);
endpoints.MapRazorPages();
});
app.UseSpa(spa =>
{
// To learn more about options for serving an Angular SPA from ASP.NET Core,
// see https://go.microsoft.com/fwlink/?linkid=864501
spa.Options.SourcePath = "ClientApp";
if (env.IsDevelopment())
{
// spa.UseAngularCliServer(npmScript: "start");
spa.UseProxyToSpaDevelopmentServer("http://localhost:4200");
}
});
CreateUserRoles(services).Wait();
}
}
解决方案
您应该添加一个用作 api 的控制器。从文档中:
[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase
此控制器中的方法将返回 StatusCode 而不是将用户重定向到视图。
更新
根据您的启动,您似乎没有设置 CompatibilityVersion:
services.AddMvc()
.SetCompatibilityVersion(CompatibilityVersion.Version_3_0);
如文件所述,这需要与 ApiController 结合使用:
前面的改动:
- 需要在控制器级别使用 [ApiController] 属性。
- 选择加入 ASP.NET Core 2.2 中引入的潜在破坏行为。
在您的情况下,假设版本 3.0:
services.AddControllers()
.AddNewtonsoftJson(options =>
{
options.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;
})
.SetCompatibilityVersion(CompatibilityVersion.Version_3_0);
推荐阅读
- c++ - 如何分配一个线程执行的时间?
- azure-devops - 有没有办法从 Release Gate REST API 调用中获取当前日期/时间
- amazon-web-services - 使用 AWS lambda 遇到“找不到模块”......有时
- npm - 无法正确安装 Gulp
- python - 采用一个参数的导入函数显示给出了 2 个参数的错误
- reactjs - React 访问原始音频数据
- c++ - cmake:使用导入语句从不同目录中的多个原始文件生成的正确方法?
- python - 使用分隔符和字符串在一行中拆分行,以使用 pandas 为一堆行
- python - 如何使用 Cython 向 Python 公开 C 结构?
- math - 哈希问题