【发布时间】:2015-03-11 16:03:07
【问题描述】:
我在我的 MVC 项目中使用表单身份验证,当使用 Visual Studio 开发服务器进行本地测试时,一切都按预期工作。一旦部署到 IIS 7.5,HTTPContext.User 就会导致 NullReferenceExceptions。
Dev 和 Prod 机器都使用相同的 SQL db(目前 - 这当然会在部署后改变)所以我知道这不是数据库或其中的数据的问题。
这必须是 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 开发服务器和 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; }
【问题讨论】:
-
项目是否有无扩展页面?在这种情况下,设置 runAllManagedModulesForAllRequests=true 可能会有所帮助。也许这个链接会帮助forums.asp.net/t/…
-
@PankajKumar 谢谢,这确实解决了它,但我在其他地方看到这不是解决任何问题的正确方法。有没有更好的替代品?
-
您似乎正在实施自己的身份验证,那么您是否也使用/实施了
FilterAttributes?为什么你忽略了IPrincipal这样的接口?似乎您正在使用 WebForms 和 MVC 的组合!这些使问题更难解决。 -
stackoverflow.com/questions/13343073/… 可能会对您有所帮助,因为您的部署区域在
machinekey配置中有所不同,而在本地您可以忽略此类选项,因为它在顶级配置文件中使用了一个选项。 -
CustomPrincipal 是一个 IPrincipal。附言当您对问题进行评分时,最好留下解释。
标签: asp.net-mvc-4 iis iis-7.5 forms-authentication nullreferenceexception