asp.net-core - ASP.NET Core:在控制器/操作级别的过滤器中返回未授权的 Json 响应
问题描述
我没有使用身份。
我有这个 ASP.NET Core 配置启用两个身份验证方案,cookie 和基本身份验证:
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
.AddCookie(options =>
{
options.Cookie.Name = "_auth";
options.Cookie.HttpOnly = true;
options.LoginPath = new PathString("/Account/Login");
options.LogoutPath = new PathString("/Account/LogOff");
options.AccessDeniedPath = new PathString("/Account/Login");
options.ExpireTimeSpan = TimeSpan.FromHours(4);
options.SlidingExpiration = true;
})
.AddScheme<AuthenticationSchemeOptions, BasicAuthenticationHandler>("BasicAuthentication", null);
BasicAuthenticationHandler
是一个自定义类,继承自AuthenticationHandler
并重写HandleAuthenticateAsync
以检查请求标头的基本身份验证质询,并返回AuthenticateResult.Fail()
或返回AuthenticateResult.Success()
票证和用户声明。
它可以正常工作:
- 带有标签的控制器/动作
[Authorize]
将检查 cookie 并重定向到不存在的登录页面。 - 带有标签的控制器/操作
[Authorize(AuthenticationSchemes = "BasicAuthentication")]
将检查标头并在不存在时回复 401 Unauthorized HTTP 代码。 - 带有标签的控制器/动作
[Authorize(AuthenticationSchemes = "BasicAuthentication,Cookies")]
将允许这两种方法访问页面,但在两次检查都失败时会以某种方式使用 Cookie 重定向机制。
我的目标是让我的大部分项目都使用 Cookies(因此将其设置为默认值),但有一些 API 类型的控制器来接受这两种方法。还应该可以标记控制器/操作以在需要时返回特定的 Json 主体(与登录重定向或基本 401 响应相反),但仅限于某些控制器。
在过去的两天里,我在 StackOverflow 上阅读了不同的类似问题和答案,似乎没有什么能满足我的需要。
以下是我找到的一些方法:
- 下面的选项
AddCookie
允许您设置某些事件,例如OnRedirectToAccessDenied
并从那里更改响应。这不起作用,因为它适用于整个项目。 - 在我
BasicAuthenticationHandler
的班级下,该AuthenticationHandler
班级允许重写HandleChallengeAsync
以从那里更改响应而不是回复 401。不幸的是,它再次全局适用于您使用该方案的任何地方,而不是控制器/操作级别。也不确定在混合多个方案时是否应用它。 - 许多答案都指向在解决方案中添加中间件,这同样会影响整个项目。
- 许多答案都指向策略,但似乎是根据声明控制用户是否有权访问资源,而不是在他没有权限时控制响应。
- 许多答案建议创建一个继承自
AuthorizeAttribute, IAuthorizationFilter
. 同样,这允许重写OnAuthorization
方法来决定用户是否有权访问资源,但不能控制在正常身份验证方案失败后的响应。
我在想要么我缺少一个过滤器类型,要么我需要创建第三种身份验证类型,它将前两者混合并控制那里的响应。找到一种在选项中添加自定义错误消息的方法也很好。
解决方案
我在评论中输入了这个......但它不适合......所以在选择解决方案之前我们可能需要明确以下几点:
- 授权过程发生在控制器上方的上层中间件
是的,AuthorizationMiddleware
在我们使用时注册app.UseAuthorization();
了,在控制器层之上,所以它在请求到达控制器之前很久就被返回了,所以,任何类型的过滤器都不能在这里应用。
- 不指定身份验证方案或策略很容易导致行为不稳定。
想象一下,身份验证过程返回一个带有请求的实例User
,但是如果权限不同会发生什么cookie
,basicAuth
比如 cookie 有myclaim
,而basicAuth
没有?两种方案的相关过程是不同的(比如挑战cookie
会导致/Account/Login
和basicAuth
导致/Login
?)。以及我们可以在每个页面上实现的各种逻辑案例。
我知道,这是不可能的,但它会变得一团糟,不是为了这些代码的作者,而是为了那些维护者。
- 客户端上某些特定进程的 Json 响应?
乍一看这可能听起来很详细,但如果在那之后提出更多的身份验证要求(如 Jwt),它会很快成为负担。在客户端覆盖这些案例中的每一个都会使用户体验非常尴尬(例如,半认证和授权)。
如果它在项目中是不可避免的。我是否可以建议创建一个默认身份验证方案,ForwardDefaultSelector
该方案将选择用于每个请求的身份验证方案。并维护一个稳定的路由HashSet
,用于检测在哪个端点上设置 Json 响应,当然AuthorizationMiddleware
,通过使用中间件。然后,我们缩小到 2 个集中的地方来检查授权。
当我们试图做一件事来做某事时,混乱就来了。至少在这种情况下,我认为在进入调试阶段时我们会更轻松。