ASP.NET MVC 自定义 IPrincipal 注入

10

我正在使用ASP.NET MVC 1.0开发一个应用程序,并尝试将自定义的IPrincipal对象注入到HttpContext.Current.User对象中。

对于传统的WebForms应用程序,我使用Application_AuthenticateRequest事件来完成此操作,如下所示。

protected void Application_AuthenticateRequest(object sender, EventArgs e)
    {
        if (HttpContext.Current.User != null)
        {
            if (HttpContext.Current.User.Identity.IsAuthenticated)
            {
                if (HttpContext.Current.User.Identity is FormsIdentity)
                {
                    // Get Forms Identity From Current User
                    FormsIdentity id = (FormsIdentity)HttpContext.Current.User.Identity;
                    // Get Forms Ticket From Identity object
                    FormsAuthenticationTicket ticket = id.Ticket;
                    // Create a new Generic Principal Instance and assign to Current User
                    SiteUser siteUser = new SiteUser(Convert.ToInt32(id.Name));

                    HttpContext.Current.User = siteUser;
                }
            }
        }

    }
使用这种方法,我能够通过将User对象显式转换为SiteUser类型来访问自定义的IPrincipal。实际上,我通过一个自定义类来实现这一点,该类继承了所有页面,并在其内部执行了相关操作。
无论如何,我的问题是,在ASP.NET MVC中,每当进行任何请求时(例如JS文件、图像等),Application_AuthenticateRequest似乎都会触发,导致应用程序崩溃。
如果您有任何帮助或建议,可以让我在ASP.NET MVC 1.0中将我的自定义IPrincipal注入到HttpContext.Current.User对象中,将不胜感激。我看到了stackoverflow上的以下帖子,但似乎不适合我想要实现的目标: ASP.NET MVC - 设置自定义IIdentity或IPrincipal TIA.

无论文件类型如何,它都不应该死亡 - 您看到了什么错误? - blowdart
每次请求资源时,我都会在Application_AuthenticateRequest方法中获得一个“命中”。相比于没有使用Application_AuthenticateRequest方法的情况下运行,这使得页面渲染非常缓慢。SiteUser对象的构造函数并没有做什么特别的事情,只是从数据库中获取用户详细信息和角色列表。 - Mr Bog
当然,你需要这样做,这是IIS7的工作方式 - 我原以为你是指出现错误而死机。 - blowdart
2个回答

8
我的问题是在ASP.NET MVC中,无论是JS文件、图片等任何请求,都会触发Application_AuthenticateRequest事件,导致应用程序崩溃。这不是MVC独有的问题 - 如果您在IIS7上运行应用程序并启用了集成管道,那么您也会遇到同样的问题。如果查找问题的原因是可扩展性,则我认为实际的问题就在于...
FormsAuthenticationTicket ticket = id.Ticket;
SiteUser siteUser = new SiteUser(Convert.ToInt32(id.Name));

我猜测你的SiteUser类会进行一些数据库查找。如果你检查一下表单认证的工作方式,票证包含生成FormsIdentity所需的所有信息(这对于角色来说不是绝对正确的,除非你特别启用了将角色缓存到cookie中)。因此,你应该考虑采用相同的方法。第一次构造siteUser对象时,在签名cookie中缓存它,然后在后续请求中使用cookie重新填充SiteUser属性。
如果你这样做,那么你可以进一步取代Thread principal,或者至少使用与SiteUser类具有相同信息的自定义IPrincipal/IUser组合。
因此,在AuthenticateRequest中,你应该有以下流程:
SiteUserSecurityToken sessionToken = null;
if (TryReadSiteUserSecurityToken(ref sessionToken) && sessionToken != null)
{
    // Call functions to attach my principal.
}
else
{
    if (HttpContext.Current.User != null && 
        HttpContext.Current.User.Identity.IsAuthenticated && 
        HttpContext.Current.User.Identity is FormsIdentity)
    {
        // Get my SiteUser object

        // Create SiteUserSecurityToken

        // Call functions to attach my principal.
    }
}

而将负责人附加的功能将包含类似以下内容的代码
HttpContext.Current.User = sessionSecurityToken.ClaimsPrincipal;
Thread.CurrentPrincipal = sessionSecurityToken.ClaimsPrincipal;
this.ContextSessionSecurityToken = sessionSecurityToken;

您需要确保将安全令牌写入cookie的函数至少添加一个校验和/MAC值,并且如果需要,可以使用机器密钥支持加密。读取函数应该验证这些值。


太好了,谢谢。这是我的低级错误!非常感谢。 - Mr Bog

1

我确实考虑过这个,但我也想以其他方式使用我的强类型用户对象。我希望不必制作自定义授权过滤器,然后在自定义基础控制器类中开始连接我的用户对象,但看起来这可能是正确的方法。我也不想实现自定义成员资格提供程序。无论如何,谢谢您的建议 :) 看起来我将被迫采取这种方法。 - Mr Bog
如果您能解释为什么要注入自定义 principal,或许还有其他更适合 ASP.NET MVC 的方法来解决您的问题。 - bzlm

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