Asp.Net Core 基于策略的授权以 401 Unauthorized 结束

4

我正在设置一个基于自定义策略的授权框架,其中将有一组业务规则来授权当前登录用户。但是,目前这个框架总是以401 未授权 结束。

以下是我的代码:

public class MyAuthorizationRequirement : IAuthorizationRequirement
{
    public MyAuthorizationRequirement()
    {       
    }
}

public class MyAuthorizationHandler : AuthorizationHandler<MyAuthorizationRequirement>
{
    public MyAuthorizationHandler()
    {
    }

    protected override void Handle(AuthorizationContext context, MyAuthorizationRequirement requirement)
    {
        context.Succeed(requirement);
    }
}

接下来在Startup.cs文件中进行以下操作。

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddSingleton<IAuthorizationHandler, MyAuthorizationHandler>()
        .AddAuthorization(options =>
        {
            options.AddPolicy("MyAuthorization",
                                policy => policy.Requirements.Add(new MyAuthorizationRequirement()));
        });
    }

这是我在我的HomeController(MVC 6)中使用它的方式。

[Authorize(Policy = "MyAuthorization")]
public class HomeController : Controller
{
    public IActionResult Index()
    {
        return View();
    }
}

当我不加Authorize属性时,Index视图可以正常渲染。但是,当我加上Authorize属性时,我只能收到空白视图。当我检查开发者工具(网络)时,我得到了以下幕后细节。

Request URL:http://localhost:51129/
Request Method:GET
Status Code:401 Unauthorized
Remote Address:[::1]:51129

我的需求和处理程序类的构造函数断点被触发,但是Handler类的Handle方法和Controller类的Index方法的断点从未被触发。


你使用了任何 身份验证中间件 吗? - adem caglin
1个回答

4
那是因为对于每个[Authorize]属性,添加到管道中的AuthorizeFilter都要求用户进行身份验证。
如果您查看源代码,即使没有调用任何策略,它也会确保用户已通过身份验证。
// Note: Default Anonymous User is new ClaimsPrincipal(new ClaimsIdentity())
if (httpContext.User == null ||
    !httpContext.User.Identities.Any(i => i.IsAuthenticated) ||
    !await authService.AuthorizeAsync(httpContext.User, context, Policy))
{
    context.Result = new ChallengeResult(Policy.AuthenticationSchemes.ToArray());
}

那个条件 httpContext.User.Identities.Any(i => i.IsAuthenticated) 对于匿名用户来说将会是 false。
还有一个 DefaultPolicyAuthorizationOptions中验证用户是否已经认证。你可以在 AddAuthorization 配置中将其设置为 null,但即使在这种情况下,上面的 AuthorizeFilter 也会确保用户已经认证。
最简单的方法就是添加一个已认证的匿名用户,这样任何匿名用户都会被分配一个已认证的 ClaimsPrincipal (因为它具有 GenericIdentity 而不是空 ClaimsIdentity)。
//Add this before app.UseMvc
app.Use(async (context, next) =>
{
    if (!context.User.Identities.Any(i => i.IsAuthenticated))
    {
        //Assign all anonymous users the same generic identity, which is authenticated
        context.User = new ClaimsPrincipal(new GenericIdentity("anonymous"));
    }
    await next.Invoke();

});

在一个真正的应用程序中,您可能会有一些身份验证框架,用户可以通过这些框架进行身份验证,这样您就不会遇到这个问题。
否则,您可能需要使用应用程序约定,并用自己修改过的实现替换AuthorizeFilter,该实现不需要已验证的用户,这个答案朝着这个方向发展。

非常感谢您花费宝贵的时间向我提供完美的指导。这是我收到过的最好的答案之一。它运行得非常完美。谢谢! - Mukesh Bhojwani
在我的实例中,我有一个完整的ASP4.5网站,涵盖了所有关于身份验证等方面的内容。这个新的ASP Core网站被添加进来,以更先进的技术来添加新页面。用户将在这两个网站之间来回导航,我想确保他已经通过身份验证并获得授权。目前我的计划是从请求中读取cookie“ASP.NET_SessionId”,并传递给业务逻辑进行授权。 - Mukesh Bhojwani
没关系,我很高兴能帮忙!我自己也在学习ASP Core,所以这对我也有好处 :) 我明白了,一旦你完成新网站的构建,你将拥有适当的身份验证,就不会遇到这个问题了。 - Daniel J.G.
我怀疑两者使用不同的HttpContexts,ASP 4.5使用System.Web.HttpContext,而ASP Core使用Microsoft.AspNet.Http.HttpContext。有一个类库项目在两个网站中都是共用的,如果我从核心网站调用类库项目中的函数,并且假设它使用上下文,则它总是将HttpContext.Current作为null。 - Mukesh Bhojwani
我的意思是,身份验证的实现可能不同,但在这两个站点中,您最终都可以填充主体/标识对象。 - Daniel J.G.
我正在使用asp.net core 1.1.2,并且遇到了上述描述的确切问题。然而,这个提议并没有起作用:我仍然一直收到401错误。有没有一种简单的方法可以在asp.net core代码中逐步调试(使用VS2017)?或者增加框架详细程度?我想了解是哪个流程导致了这个401错误。 - CanardMoussant

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接