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

时间:2015-03-11 16:03:07

标签: asp.net-mvc-4 iis iis-7.5 forms-authentication nullreferenceexception

我在我的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" />

**** ****剪断                                                                                                                                                                                                                                                                                                                                                                                             

<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 Development Server和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;
        }
    }
}

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

int userT = User.Team.TeamId;

在此上下文中,用户为CustomPrincipal BaseController.User

EDIT2:

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

EDIT3

自定义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; }

2 个答案:

答案 0 :(得分:1)

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

<强>解释

HttpContext是应用程序的运行时状态的一部分。在现代托管环境(IIS集成管道模式和OWIN)中,HttpContext方法完成后才会填充Application_Start。您需要HttpContext.User的任何行为都不应在Application_BeginRequest事件之后或之后执行。

参考:Request is not available in this context

答案 1 :(得分:1)

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

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

如您所知,由于配置系统基于使用** .config *文件的管理系统的分层系统,您应该考虑默认值,可能是<remove/><add/>一些参数。 IIS 7及更高版本的配置文件位于%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 将不起作用或可能导致安装摘要认证模块之前的错误。

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

更新关于 SlidingExpiration:根据MSDN

  

滑动过期会重置有效的过期时间   如果发出请求,则认证cookie超过一半   超时间隔已经过去。

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