HttpContext.User NullReferenceException 仅在已部署的服务器上

HttpContext.User NullReferenceException only on deployed server

提问人:mitchellJ 提问时间:3/12/2015 最后编辑:abatishchevmitchellJ 更新时间:4/17/2022 访问量:1914

问:

我在我的 MVC 项目上使用 formsauthentication,当使用 Visual Studio Development Server 进行本地测试时,一切按预期工作。部署到 IIS 7.5 后,会导致 s.HTTPContext.UserNullReferenceException

Dev 和 Prod 机器都使用相同的 SQL 数据库(目前 - 当然,这将在部署后发生变化),所以我知道这不是数据库或其中数据的问题。

这必须是 IIS 或我的 web.config 中的设置,但我找不到它。 我已经尝试了对我的web.config进行各种更改(来自我在SE周围找到的建议),这是我当前实现的web.config的一部分:

<appSettings>
    <add key="autoFormsAuthentication" value="true" />
    <add key="enableSimpleMembership" value="false" />
    <add key="webpages:Version" value="2.0.0.0" />
    <add key="webpages:Enabled" value="false" />
    <add key="PreserveLoginUrl" value="true" />
    <add key="ClientValidationEnabled" value="true" />

****Snip****

<system.web>
    <httpRuntime targetFramework="4.5" />
    <compilation debug="true" targetFramework="4.5" />
    <authentication mode="Forms">
      <forms loginUrl="~/Account/Login" timeout="2880" cookieless="UseCookies"/>
    </authentication>
    <pages>
      <namespaces>
        <add namespace="System.Web.Helpers" />
        <add namespace="System.Web.Mvc" />
        <add namespace="System.Web.Mvc.Ajax" />
        <add namespace="System.Web.Mvc.Html" />
        <add namespace="System.Web.Optimization" />
        <add namespace="System.Web.Routing" />
        <add namespace="System.Web.WebPages" />
        <add namespace="ProjectSquid.WebUI.HTMLHelpers" />
      </namespaces>
    </pages>
    <roleManager enabled="true" defaultProvider="CustomRoleProvider">
      <providers>
        <clear />
        <add name="CustomRoleProvider" 
             type="Project.Domain.Filters.CustomRoleProvider"
             connectionStringName="EFDbContext"
             enablePasswordRetrieval="false"
             cacheRolesInCookie="true"/>
      </providers>
    </roleManager>
    <membership defaultProvider="SimpleMembershipProvider">
      <providers>
        <clear />
        <add name="SimpleMembershipProvider" type="WebMatrix.WebData.SimpleMembershipProvider, WebMatrix.WebData" />
      </providers>
    </membership>
    <sessionState mode="InProc" customProvider="DefaultSessionProvider">
      <providers>
        <add name="DefaultSessionProvider" type="System.Web.Providers.DefaultSessionStateProvider, System.Web.Providers, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" connectionStringName="EFDbContext" />
      </providers>
    </sessionState>

<system.webServer>
    <httpProtocol>
      <customHeaders>
        <add name="X-UA-Compatible" value="IE=9" />
      </customHeaders>
    </httpProtocol>
    <validation validateIntegratedModeConfiguration="false" />
    <handlers>
      <remove name="ExtensionlessUrlHandler-ISAPI-4.0_32bit" />
      <remove name="ExtensionlessUrlHandler-ISAPI-4.0_64bit" />
      <remove name="ExtensionlessUrlHandler-Integrated-4.0" />
      <add name="ExtensionlessUrlHandler-ISAPI-4.0_32bit" path="*." verb="GET,HEAD,POST,DEBUG,PUT,DELETE,PATCH,OPTIONS" modules="IsapiModule" scriptProcessor="%windir%\Microsoft.NET\Framework\v4.0.30319\aspnet_isapi.dll" preCondition="classicMode,runtimeVersionv4.0,bitness32" responseBufferLimit="0" />
      <add name="ExtensionlessUrlHandler-ISAPI-4.0_64bit" path="*." verb="GET,HEAD,POST,DEBUG,PUT,DELETE,PATCH,OPTIONS" modules="IsapiModule" scriptProcessor="%windir%\Microsoft.NET\Framework64\v4.0.30319\aspnet_isapi.dll" preCondition="classicMode,runtimeVersionv4.0,bitness64" responseBufferLimit="0" />
      <add name="ExtensionlessUrlHandler-Integrated-4.0" path="*." verb="GET,HEAD,POST,DEBUG,PUT,DELETE,PATCH,OPTIONS" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0" />
    </handlers>
    <modules runAllManagedModulesForAllRequests="false">
      
      <remove name="FormsAuthentication" />
      <remove name="DefaultAuthentication" />
      <add name="FormsAuthentication" type="System.Web.Security.FormsAuthenticationModule" preCondition="" />
      <add name="DefaultAuthentication" type="System.Web.Security.DefaultAuthenticationModule" preCondition="" />
      
      <remove name="UrlRoutingModule-4.0"/>
      <add name="UrlRoutingModule-4.0" type="System.Web.Routing.UrlRoutingModule, System.Web, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" preCondition="" />
      <add name="ErrorLog" type="Elmah.ErrorLogModule, Elmah" preCondition="managedHandler" />
      <add name="ErrorMail" type="Elmah.ErrorMailModule, Elmah" preCondition="managedHandler" />
      <add name="ErrorFilter" type="Elmah.ErrorFilterModule, Elmah" preCondition="managedHandler" />
    </modules>
  </system.webServer>

什么可能导致与 VS Development Server 和 IIS 7.5 实现不同?HttpContext.User

编辑:

HttpContext通过继承的 BaseController 馈送:

protected virtual new CustomPrincipal User
{
    get { return HttpContext.User == null? null : HttpContext.User as CustomPrincipal; }
}

public new HttpContextBase HttpContext
{
    get
    {
        return ControllerContext == null ? null : ControllerContext.HttpContext;
    }
}

在 PostAuthenticationRequest 之前不会创建 cookie:

 public void MvcApplication_PostAuthenticationRequest(object sender, EventArgs e)
{

    var authCookie = HttpContext.Current.Request.Cookies[FormsAuthentication.FormsCookieName];
    if (authCookie != null)
    {

        string encTicket = authCookie.Value;
        if (!String.IsNullOrEmpty(encTicket))
        {
            
            var ticket = FormsAuthentication.Decrypt(encTicket);
            var id = new UserIdentity(ticket);
            string[] userRole = Roles.GetRolesForUser(id.Name);
            var prin = new CustomPrincipal(id);
            HttpContext.Current.User = prin;
            Thread.CurrentPrincipal = prin;
        }
    }
}

身份验证本身似乎工作正常,因为导致异常的函数开始并成功开始执行,但在到达第一个用户引用时失败:[Authorize]null

int userT = User.Team.TeamId;

在此上下文中,用户是 .CustomPrincipalBaseController.User

编辑2:

<authentication mode="Forms">
  <forms loginUrl="~/Account/Login" timeout="2880" 
         cookieless="UseCookies"
         name=".ASPXAUTH"
         protection="All"
         slidingExpiration="true"/>
</authentication>

编辑3

习惯:IIdentity

 [Serializable]
    public class UserIdentity : MarshalByRefObject, IIdentity
    {
        private readonly FormsAuthenticationTicket _ticket;


        public UserIdentity(FormsAuthenticationTicket ticket)
        {
            _ticket = ticket;
        }

        public string AuthenticationType
        {
            get { return "Custom"; }
        }

        public bool IsAuthenticated
        {
            get { return !string.IsNullOrEmpty(this.Name); }
        }

        public string Name
        {
            get { return _ticket.Name; }
        }

        public string UserId
        {
            get { return _ticket.UserData; }
        }

        public bool IsInRole(string Role)
        {
            return Roles.IsUserInRole(Role);
        }

        public IIdentity Identity
        {
            get { return this; }
        }


public void GetObjectData(SerializationInfo info, StreamingContext context)
    {
        if (context.State == StreamingContextStates.CrossAppDomain)
        {
            GenericIdentity gIdent = new GenericIdentity(this.Name, this.AuthenticationType);
            info.SetType(gIdent.GetType());

            System.Reflection.MemberInfo[] serializableMembers;
            object[] serializableValues;

            serializableMembers = FormatterServices.GetSerializableMembers(gIdent.GetType());
            serializableValues = FormatterServices.GetObjectData(gIdent, serializableMembers);

            for (int i = 0; i < serializableMembers.Length; i++)
            {
                info.AddValue(serializableMembers[i].Name, serializableValues[i]);
            }
        }
        else
        {
            throw new InvalidOperationException("Serialization not supported");
        }
    }

习惯:IPrincipal

interface ICustomPrincipal : IPrincipal
{
    int Id { get; set; }
    string Name { get; set; }
    string Role { get; set; }
}

public class CustomPrincipal : IPrincipal
{
    public CustomPrincipal(UserIdentity identity)
    {
        this.Identity = identity;
    }

    public IIdentity Identity { get; private set; }
C# ASP.NET-MVC-4 IIS 表单身份验证 NullReferenceException

评论

1赞 Pankaj Kumar 3/23/2015
项目是否有无扩展名页面?在这种情况下,设置 runAllManagedModulesForAllRequests=true 可能会有所帮助。也许这个链接会帮助 forums.asp.net/t/......
0赞 mitchellJ 3/23/2015
@PankajKumar谢谢,这确实解决了它,但我在其他地方看到这不是解决任何问题的正确方法。有没有更好的选择来做同样的事情?
0赞 Amirhossein Mehrvarzi 3/25/2015
您似乎正在实施自己的身份验证,那么您是否也使用/实施了身份验证?为什么你忽略了像这样的接口?您似乎正在使用 WebForms 和 MVC 的组合!这些使问题更加复杂。FilterAttributeIPrincipal
0赞 Amirhossein Mehrvarzi 3/25/2015
stackoverflow.com/questions/13343073/......可能会对您有所帮助,因为您的部署区域在本地的配置不同,您可以忽略这些选项,因为它在顶级配置文件中使用了一个选项。machinekey
0赞 mitchellJ 3/25/2015
CustomPrincipal 是一个 IPrincipal。P.S. 当你降低一个问题时,留下一个解释是很好的做法。

答:

1赞 NightOwl888 3/25/2015 #1

最有可能的是,您正在尝试在初始化之前进行检索。此行为在 IIS 经典模式(或 Visual Studio Web 服务器)和 IIS 集成管道模式之间有所不同,这可以解释为什么在环境之间看到不同的行为。HttpContext.User

解释

HttpContext是应用程序运行时状态的一部分。在新式宿主环境(IIS 集成管道模式和 OWIN)中,直到方法完成之后才会填充。在事件发生之前或之后,不应执行所需的任何行为。HttpContextApplication_StartHttpContext.UserApplication_BeginRequest

参考:请求在此上下文中不可用

评论

0赞 mitchellJ 3/25/2015
但此时Application_Start应该已经被触发了,对吧?我的控制器工厂在Application_Start中启动,直到身份验证后才删除数据。
0赞 NightOwl888 3/25/2015
好吧,您还没有在应用程序中发布您收到错误的位置,所以这有点黑暗。但症状是合适的。如果使用 DI,是否要访问某个服务的构造函数的调用堆栈中的 HttpContext.User 属性?请注意,您不能这样做。有关解决方案,请参阅此答案
0赞 NightOwl888 3/25/2015
请注意,可以通过将本地环境设置为在 IIS 集成模式下运行来证明或反驳此理论。如果这导致开发环境中发生错误,则可以更轻松地跟踪问题。
0赞 mitchellJ 3/25/2015
我在 VS Dev Server 上找到的所有信息都说它不能切换到 Integrated Pipeline。你知道切换它的方法吗?
0赞 NightOwl888 3/25/2015
需要将其临时托管在本地 IIS 下,才能启用集成模式(在应用池中)。VS Dev Server 没有该选项。
1赞 Amirhossein Mehrvarzi 3/25/2015 #2

从您的帖子中尚不清楚,因为配置身份验证取决于项目中的各种设置以及配置文件。例如,在 Web.config 文件中,有几个位置可以自定义/配置身份验证,例如您尚未在帖子中放置的以下位置(最重要的规则):

<system.web>
   <authentication mode="" />
</system.web>

如您所知,由于配置系统基于使用 **.config* 文件的管理系统的分层系统,因此您应该考虑默认值,可能是 by 或某些参数。IIS 7 及更高版本的配置文件位于您的文件夹中,主要配置文件包括:<remove/><add/>%WinDir%\System32\Inetsrv\Config

  • ApplicationHost.config - 此配置文件存储所有网站和应用程序的设置。
  • Administration.config - 此配置文件存储 IIS 管理的设置。这些设置包括 为 IIS 管理器工具安装的管理模块,作为 以及管理模块的配置设置。
  • Redirection.config - IIS 7 及更高版本支持从单个集中式配置文件管理多个 IIS 服务器。 此配置文件包含指示 集中式配置文件的存储位置。

注意:某些设置可以委派给 Web.config 文件,这些文件可能会覆盖 ApplicationHost.config 文件中的设置。此外,不能将未委派的设置添加到 Web.config 文件中。

提示: IIS 7 的默认安装不包含摘要式身份验证,因此在安装摘要式身份验证模块之前,将摘要式身份验证的设置添加到 ApplicationHost.config 将不起作用或可能导致错误。

您需要同时查看本地配置和部署配置才能满足您的目的。如果您在使用集成管道时遇到问题,请参阅以下页面以利用其优势:

更新:根据MSDNSlidingExpiration

滑动过期将重置有效 身份验证 cookie(如果发出请求)和超过一半的 超时间隔已过。

如果 Cookie 过期,用户必须重新进行身份验证。将该属性设置为 可以根据配置的超时值限制身份验证 Cookie 的有效时间,从而提高应用程序的安全性。 所以我认为没有必要把它当作的。这意味着,如果在此时间段内未发出任何请求,它将在激活缓存的时间段后过期缓存。当有太多数据要缓存时,这种类型的过期非常有用。因此,它会将应用程序中经常使用的那些项目放入缓存中。因此,它不会使用不必要的内存。SlidingExpirationfalse

评论

0赞 mitchellJ 3/25/2015
将我的身份验证模式设置添加为 Edit2。
0赞 Amirhossein Mehrvarzi 3/25/2015
@GamerJ5我也更新了我的答案,但是我们需要更完整的细节来解决这个问题,例如有用的堆栈跟踪等......