派生自AuthorizeAttribute但User.Identity.Name为null,除非使用AuthorizeAttribute

时间:2015-10-15 11:37:20

标签: asp.net-mvc authorization windows-authentication

所以我创建了一个自定义授权属性,我在一些地方使用了这个属性,该属性派生自一个派生自AuthorizeAttribute的抽象基类:

CustomAuthorizeAttributeBase.cs

public abstract class CustomAuthorizeAttributeBase : AuthorizeAttribute
{
    public abstract string GetUsers();
    public abstract string GetRoles();
    protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
    {
        if (filterContext.IsChildAction)
        {
            return;
        }
        filterContext.Result =
            new RedirectToRouteResult(new RouteValueDictionary
            {
                {"controller", "NotAuthorized"},
                {"action", "Index"},
            });
    }

    protected override bool AuthorizeCore(HttpContextBase httpContext)
    {
        if (GetUsers().IndexOf(httpContext.User.Identity.Name, StringComparison.CurrentCultureIgnoreCase) >= 0 ||
            GetRoles().Split(',').Any(s => httpContext.User.IsInRole(s)))
        {
            return true;
        }
        return false;
    }

}

AreaLevelReadonly.cs

public class AreaLevelReadOnly : CustomAuthorizeAttributeBase
{
    public override string GetUsers()
    {
        return ConfigurationManager.AppSettings["AreaReadonlyUsers"];
    }

    public override string GetRoles()
    {
        return ConfigurationManager.AppSettings["AreaReadonlyRoles"];
    }
}

我还有一些非常简单的代码可以让我获得当前登录的用户:

UserIdentity.cs

public class UserIdentity : IUserIdentity
{

    public string GetUserName()
    {
        return HttpContext.Current.User.Identity.Name.Split('\\')[1];
    }
}

但是,当我将AreaLevelReadonly属性添加到控制器时,getUserName失败并返回Name为空的异常。在将authorize属性放在那里之前,我痛苦了大约一个小时,此时它神奇地开始再次工作。那么,在我的属性派生自authorizeattribute的实现级别上有什么不同之处并不会导致Name被填充。

注意:该区域启用了Windows身份验证,代码可以运行,但我不明白为什么Readonly属性不足以触发HttpContext.Current.User.Identity.Name的授权和填充。< / p>

编辑:工作:

[AreaLevelReadonly]
[Authorize]
public class DeleteAreaDataController : Controller { 
    //etc 
    var username = _userIdentity.GetUserName(HttpContext);
    //etc
}

名称上的例外:

[AreaLevelReadonly]
public class DeleteAreaDataController : Controller { 
    //etc
    var username = _userIdentity.GetUserName(HttpContext);
    //etc
}

2 个答案:

答案 0 :(得分:0)

更有可能的是,您在填充User.Identity.Name之前就已经访问了Authorize。通过包含标准User.Identity.Name属性,您的代码只有在用户获得授权并且已填充User.Identity.Name后才会运行。

修改

抱歉,我误解了试图调用GetUserAccount(HttpContext)的代码在哪里运行。基于您在自定义属性中发生的信念,我建议您过早尝试访问它。但是,我现在看到你在你的控制器中调用它了(虽然对User.Identity.Name中发生的事情的解释会有所帮助。)

无论如何,您的自定义属性显然会增加用户是否获得授权的额外条件。当您返回false时,没有用户。这不是用户登录的情况&#34;但不允许看到该页面。它是存在还是不存在。因此,用户根据您的自定义属性失败授权(Authorize为空),但当您包含User.Identity.NameGetUserName有值)时,授权。< / p>

多长时间,当用户授权失败时,您的GetUserAccountUser.Identity.Name或任何代码都需要考虑。或者,如果用户不应该未通过授权,则您需要查看自定义属性无法正常工作的原因。但无论如何,您仍然应该将=VLOOKUP($A20;Employees!$A:$GE;COLUMN(C:C);FALSE)视为空。

答案 1 :(得分:0)

在您检查用户是否通过身份验证之前,您的自定义属性可能正在读取User.Identity.Name

换句话说,在IsAuthorized()中,在阅读User.Identity.Name之前,您应该执行以下操作:

if (!user.Identity.IsAuthenticated)
{
    // Your custom code...
    return false;
}

您之所以需要这样做,是因为Windows身份验证(至少对于NTLM而言)是客户端和服务器之间的两步协商过程(请参见https://support.citrix.com/article/CTX221693)。将有2个请求-第一个没有名称,第二个带有名称。您可以自己进行测试-here提供了AuthorizeAttribute的源代码。将其复制/粘贴到代码中,然后在IsAuthorized中放置一个断点-您将看到该断点被击中两次。第一次,该名称为null,第二次,该名称设置为您的用户名。

因此,我认为解决方案是在方法开始时检查user.Identity.IsAuthenticated,如果您需要运行自定义代码(如上所示),或者仅需要return false,只需将上面的代码替换为base.IsAuthorized()即可,