簡體   English   中英

HttpContext.User NullReferenceException 僅在部署的服務器上

[英]HttpContext.User NullReferenceException only on deployed server

我在我的 MVC 項目上使用 formsauthentication,當使用 Visual Studio Development Server 在本地進行測試時,一切都按預期工作。 一旦部署到 IIS 7.5, HTTPContext.User就會導致NullReferenceException s。

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>

是什么導致HttpContext.User與 VS 開發服務器和 IIS 7.5 實現不同?

編輯:

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;
        }
    }
}

身份驗證本身似乎工作正常,因為導致異常的 function 以[Authorize]開頭並成功開始執行,但在到達第一個用戶引用時失敗為null

int userT = User.Team.TeamId;

在這種情況下,用戶是CustomPrincipal BaseController.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; }

很可能,您正試圖在HttpContext.User初始化之前檢索它。 此行為在 IIS 經典(或 Visual Studio Web 服務器)和 IIS 集成管道模式之間有所不同,這可以解釋為什么您在環境之間看到不同的行為。

解釋

HttpContext是應用程序運行時的一部分 state 在現代托管環境(IIS 集成管道模式和 OWIN)中,直到Application_Start方法完成后才會填充HttpContext 您擁有的任何需要HttpContext.User的行為都不應在Application_BeginRequest事件或之后執行。

參考: 請求在此上下文中不可用

從您的帖子中不清楚,因為配置身份驗證取決於項目中的各種設置以及配置文件。 例如在Web.config文件中,有幾個地方可以像這樣自定義/配置身份驗證(最重要的規則),但您還沒有放在帖子中:

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

如您所知,由於配置系統基於使用 **.config* 文件的管理系統的分層系統,因此您應該考慮默認值,也許通過<remove/><add/>一些參數。 IIS 7 及更高版本的配置文件位於您的%WinDir%\System32\.netsrv\Config文件夾中,主要配置文件為:

  • ApplicationHost.config - 此配置文件存儲所有 Web 站點和應用程序的設置。
  • Administration.config - 此配置文件存儲 IIS 管理的設置。 這些設置包括為 IIS Manager 工具安裝的管理模塊列表,以及管理模塊的配置設置。
  • Redirection.config - IIS 7 及更高版本支持從單個集中配置文件管理多個 IIS 服務器。 此配置文件包含指示集中配置文件存儲位置的設置。

注意:某些設置可以委托給Web.config文件,這些文件可能會覆蓋ApplicationHost.config文件中的設置。 此外,未委托的設置無法添加到Web.config文件中。

提示:IIS 7 的默認安裝不包含 Digest 身份驗證,因此在安裝 Digest 身份驗證模塊之前,將 Digest 身份驗證的設置添加到ApplicationHost.config將不起作用或可能導致錯誤。

您需要查看本地和部署配置以滿足您的目的。 如果您在使用集成管道時遇到問題,請參閱以下頁面以利用它的優勢:

關於SlidingExpiration的更新:根據MSDN

如果發出請求並且超過一半的超時間隔已過去,則滑動過期會重置有效身份驗證 cookie 的過期時間。

如果 cookie 過期,用戶必須重新進行身份驗證。 SlidingExpiration屬性設置為false可以根據配置的超時值限制身份驗證 cookie 的有效時間,從而提高應用程序的安全性。 所以我認為沒有必要將它用作false 這意味着如果在該時間段內沒有任何請求,它將在激活緩存時的時間段后使緩存過期。 當有太多數據要緩存時,這種類型的過期很有用。 所以它將那些在應用程序中經常使用的項目放入緩存中。 所以它不會使用不必要的 memory。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM