【问题标题】:MVC3 Windows Authentication override User.IdentityMVC3 Windows 身份验证覆盖 User.Identity
【发布时间】:2012-09-16 02:43:00
【问题描述】:

我正在使用带有 MSSQL 后端的 MVC3 构建 Intranet 应用程序。我的身份验证和角色(通过自定义角色提供程序)正常工作。我现在要做的是覆盖 User.Identity 以允许像 User.Identity.FirstName 这样的项目。但我找不到任何代码可以告诉我如何在 WindowsIdentity 中执行此操作

我尝试过编写自定义提供程序:

public class CPrincipal : WindowsPrincipal
{
    UserDAL userDAL = new UserDAL();
    public CPrincipal(WindowsIdentity identity)
        : base(identity)
    {
        userInfo = userDAL.GetUserProfile(identity.Name.Split('\\')[1]);
        this.identity = identity;
    }
    public UserInfo userInfo { get; private set; }
    public WindowsIdentity identity { get; private set; }
}

并覆盖 WindowsAuthentication 以填充自定义主体。

    void WindowsAuthentication_OnAuthenticate(object sender, WindowsAuthenticationEventArgs e)
    {
        if (e.Identity != null && e.Identity.IsAuthenticated)
        {
            CPrincipal cPrincipal = new CPrincipal(e.Identity);
            HttpContext.Current.User = cPrincipal;
        }
    }

我在身份验证函数中有一个断点,并且正在填充主体;但是,当我在控制器中放置断点时,用户只是其正常的 RolePrincipal,而不是我的自定义主体。我做错了什么?

编辑:

我在 global.asax 中注释掉了上面的代码。 我已经使用 C# 覆盖了 AuthorizeAttribute:

public class CAuthorize : AuthorizeAttribute
{
    protected override bool AuthorizeCore(HttpContextBase httpContext)
    {
        bool authorized = base.AuthorizeCore(httpContext);
        if (!authorized)
        {
            return false;
        }


        IIdentity user = httpContext.User.Identity;
        CPrincipal cPrincipal = new CPrincipal(user);
        httpContext.User = cPrincipal;

        return true;
    } 

}

并将我的委托人调整为以下内容:

public class CPrincipal : IPrincipal
{
    private UserDAL userDAL = new UserDAL();
    public CPrincipal(IIdentity identity)
    {
        userInfo = userDAL.GetUserProfile(identity.Name.Split('\\')[1]);
        this.Identity = identity;
    }
    public UserInfo userInfo { get; private set; }

    public IIdentity Identity { get; private set; }

    public bool IsInRole(string role)
    {
        throw new NotImplementedException();
    }
}

现在我在输入断点时,手表会在用户中显示以下内容:

  • 用户
    • [CSupport.Model.CPrincipal]
    • 身份

身份是可访问的;但是,它仍然是 WindowsIdentity CPrincipal 只能在手表中访问,不能直接访问。

编辑: 感谢为此做出贡献的所有人。您大大扩展了我对各个部分如何工作的理解。

我有两种工作方式,所以我想我会分享。

选项 1:覆盖 Global.asax 中的授权请求

这是我要去的那个。

我没有使用 Application_AuthenticateRequest,因为(据此:HttpContext.Current.User is null even though Windows Authentication is on)用户尚未在 Windows 身份验证过程中填充,因此我无法使用任何东西来获取用户信息。

Application_AuthorizeRequest 是链中的下一个,在引入 windows 身份之后发生。

    protected void Application_AuthorizeRequest(object sender, EventArgs e)
    {
        if (User.Identity.IsAuthenticated && Roles.Enabled)
        {
            Context.User = new FBPrincipal(HttpContext.Current.User.Identity);
        }
    }

这是 Principal 的覆盖

public class CPrincipal : IPrincipal
{
    private UserDAL userDAL = new UserDAL();
    public CPrincipal(IIdentity identity)
    {
        userInfo = userDAL.GetUserProfile(identity.Name.Split('\\')[1]);
        this.Identity = identity;
    }
    public UserInfo userInfo { get; private set; }

    public IIdentity Identity { get; private set; }

    public bool IsInRole(string role)
    {
        return userDAL.IsUserInRole(userInfo.UserName, role);
    }
}

这是您在新创建的 Principal 中访问更新信息的方式。

    [Authorize(Roles = "super admin")]
    public ActionResult Dashboard()
    {
        string firstname = (User as CPrincipal).userInfo.FirstName; // <--
        DashboardModel dModel = reportDAL.GetChartData();
        return View(dModel);
    }

选项 2:覆盖 AuthorizeAttribute

这是被覆盖的Principal(和上面一样)

public class CPrincipal : IPrincipal
{
    private UserDAL userDAL = new UserDAL();
    public CPrincipal(IIdentity identity)
    {
        userInfo = userDAL.GetUserProfile(identity.Name.Split('\\')[1]);
        this.Identity = identity;
    }
    public UserInfo userInfo { get; private set; }

    public IIdentity Identity { get; private set; }

    public bool IsInRole(string role)
    {
        return userDAL.IsUserInRole(userInfo.UserName, role);
    }
}

这里是授权属性的覆盖

public class CAuthorize : AuthorizeAttribute
{
    protected override bool AuthorizeCore(HttpContextBase httpContext)
    {
        bool authorized = base.AuthorizeCore(httpContext);
        if (!authorized)
        {
            return false;
        }


        IIdentity user = httpContext.User.Identity;
        CPrincipal cPrincipal = new CPrincipal(user);
        httpContext.User = cPrincipal;

        return true;
    } 

}

您可以在此处更改要使用的 AuthorizeAttribute 并利用新信息。

    [CAuthorize(Roles = "super admin")] // <--
    public ActionResult Dashboard()
    {
        string firstname = (User as CPrincipal).userInfo.FirstName; // <--
        DashboardModel dModel = reportDAL.GetChartData();
        return View(dModel);
    }

选项 1 在全局范围内处理所有事情,选项 2 在个人级别处理所有事情。

【问题讨论】:

  • 等等...那个事件处理程序是什么?您不是在尝试使用 ASP.NET 登录控件吗?此活动位于何处以及与哪个活动相关?
  • 我在 Intranet 站点上使用 Windows 身份验证。此事件处理程序位于 global.asax
  • global.asax 中没有 OnAuthenticate 处理程序。这可能就是您遇到问题的原因。
  • 使用时必须将其转换为 WindowsIdentity。或者,使用我在下面制作的扩展方法。
  • 如果我将身份转换为 CPrincipal 中的 WindowsIdentity,则会出现以下错误:“CSupport.Models.CPrincipal.Identity”无法实现“System.Security.Principal.IPrincipal.Identity”,因为它确实实现了没有“System.Security.Principal.IIdentity”的匹配返回类型如果我将覆盖更改为 WindowsPrincipal,我会收到此错误:“System.Security.Principal.WindowsPrincipal”不包含采用 0 个参数的构造函数

标签: asp.net-mvc-3 windows-authentication intranet windows-identity windows-principal


【解决方案1】:

您应该重写 global.asax 中的 Application_AuthenticateRequest 方法,而不是这样做,然后使用 Current.User 而不是 HttpContext.Current.User(不知道为什么,但有区别)。

那么,在控制器中访问它的一种简单方法是创建一个扩展方法?像这样的:

public static class IIdentityExtensions {
    public static IMyIdentity MyIdentity(this IIdentity identity) {
        return (IMyIdentity)identity;
    }
}

那么你可以直接说User.Identity.IMyIdenty().FirstName。您也可以将其作为属性来执行。

这是我使用的代码:

protected void Application_AuthenticateRequest(Object sender, EventArgs e)
{
    FormsAuthenticationTicket authTicket = FormsAuthentication
       .Decrypt(authCookie.Value);
    var identity = new MyIdentity(authTicket.Name, "Forms", 
       FormsAuthenticationHelper.RetrieveAuthUserData(authTicket.UserData));
    Context.User = new GenericPrincipal(identity, 
       DependencyResolver.Current.GetService<ISecurityHandler>()
          .GetRoles(identity.Name).ToArray());
}

现在,忽略 DependencyResolver 的东西和自定义的身份验证票的东西,这是非常基本的,对我来说可以正常工作。

然后,在我的应用程序中,当我需要来自我的自定义身份的信息时,我只需使用 ((IMyIdentity)User.Identity).FirstName 或我需要的任何内容进行投射。这不是火箭科学,但它确实有效。

【讨论】:

  • 我只尝试了 authenticaterequest 部分并得到:HttpContext.Current.User 为 null 并且 Current 不存在。 protected void Application_AuthenticateRequest(object sender, EventArgs e) { if (HttpContext.Current.User.Identity != null &amp;&amp; HttpContext.Current.User.Identity.IsAuthenticated) { HttpContext.Current.User = new CPrincipal(HttpContext.Current.User.Identity); } }
  • @TobyJones - 查看我的编辑。在 AuthenticateRequest 中进行身份验证是您的工作。 User 当然是 N​​ull,因为还没有进行身份验证。
【解决方案2】:

我做错了什么?

[Authorize] 属性可能会覆盖您的更改。因此,不要在 Global.asaxWindowsAuthentication_OnAuthenticate 方法中执行此操作,而是编写自定义 Authorize 属性,如下所示:

public class MyAuthorizeAttribute : AuthorizeAttribute
{
    protected override bool AuthorizeCore(HttpContextBase httpContext)
    {
        var authorized = base.AuthorizeCore(httpContext);
        if (!authorized)
        {
            return false;
        }


        var user = httpContext.User as WindowsIdentity;
        CPrincipal cPrincipal = new CPrincipal(user);
        httpContext.User = cPrincipal;

        return true;
    }
}

然后使用您的自定义属性而不是默认属性:

[MyAuthorize]
public ActionResult SomeAction()
{
    // User.Identity will be your custom principal here
}

在 ASP.NET MVC 中,执行授权的标准方法是通过授权操作过滤器,而不是通过 Global.asax 中的事件。

【讨论】:

  • Authorize 属性不会覆盖您的上下文。
  • 无论如何,在 ASP.NET MVC 应用程序中使用 Global.asax 中的事件来执行授权是不好的做法。您应该改用自定义授权过滤器。
  • 他没有在他的代码中进行授权,他只是在应用他的自定义主体,这仍然应该在 global.asax 中完成。
  • 不,我认为在 Global.asax 中应用自定义 Principal 不是一个好主意。此代码完全无法单独测试,并且很难仅在某些控制器操作上有条件地应用。如果您只想在某些区域应用授权怎么办? Global.asax 不是在 ASp.NET MVC 应用程序中执行授权的正确方法。这当然是我的看法。我尊重您的授权,您可以随意授权。
  • 好吧,我从来没有遇到过在应用程序的不同部分使用不同类型的主体的应用程序,但我想如果那是你需要的,当然可以。但是,我觉得应该尽早在事件链中设置自定义主体,以防止出现一个事件看到一个主体而另一个事件看到另一个主体的情况。围绕它创建一个可测试的包装器相对容易,但这就是它作为接口的意义所在。但是,是的,我想我们对此有不同的看法。
猜你喜欢
  • 1970-01-01
  • 2015-12-18
  • 1970-01-01
  • 1970-01-01
  • 2021-09-08
  • 2019-01-28
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多