18

我在代码中实现了一个自定义 Profile 对象,如 Joel 所述:

如何分配配置文件值?

但是,当我创建新用户时,我无法让它工作。当我这样做时:

Membership.CreateUser(userName, password);
Roles.AddUserToRole(userName, "MyRole");

用户已创建并添加到数据库中的角色,但HttpContext.Current.User仍然为空,并Membership.GetUser()返回 null,因此(来自 Joel 的代码)不起作用:

static public AccountProfile CurrentUser
{
    get { return (AccountProfile)
                     (ProfileBase.Create(Membership.GetUser().UserName)); }
}

AccountProfile.CurrentUser.FullName = "Snoopy";

我尝试过Membership.GetUser(userName)以这种方式调用和设置 Profile 属性,但设置的属性仍然为空,并且调用AccountProfile.CurrentUser(userName).Save()不会将任何内容放入数据库中。我还尝试通过调用Membership.ValidateUser,FormsAuthentication.SetAuthCookie等指示用户有效并已登录,但当前用户仍然为 null 或匿名(取决于我的浏览器 cookie 的状态)。

已解决(进一步编辑,见下文):根据 Franci Penov 的解释和更多实验,我发现了这个问题。Joel 的代码和我尝试的变体只适用于现有的配置文件。如果 Profile 不存在,ProfileBase.Create(userName)每次调用都会返回一个新的空对象;您可以设置属性,但它们不会“粘住”,因为每次访问它时都会返回一个新实例。设置HttpContext.Current.User为 newGenericPrincipal 将为您提供一个 User 对象,但不是Profile 对象,并且ProfileBase.Create(userName)仍将HttpContext.Current.Profile指向新的空对象。

如果要在同一请求中为新创建的用户创建 Profile,则需要调用HttpContext.Current.Profile.Initialize(userName, true). 然后,您可以填充初始化的配置文件并保存它,它可以在以后的请求中按名称访问,因此 Joel 的代码将起作用。我在内部使用HttpContext.Current.Profile,当我需要在创建后立即创建/访问配置文件时。在任何其他请求中,我使用ProfileBase.Create(userName),并且我只将该版本公开。

请注意,Franci 是正确的:如果您愿意在第一次往返时创建用户(和角色)并将其设置为已验证,并要求用户然后登录,您将能够更简单地访问配置文件在后续请求中通过 Joel 的代码。让我感到震惊的是,角色可以在用户创建时立即访问,而无需任何初始化,但 Profile 不是。

我的新 AccountProfile 代码:

public static AccountProfile CurrentUser
{
    get
    {
        if (Membership.GetUser() != null)
            return ProfileBase.Create(Membership.GetUser().UserName) as AccountProfile;
        else
            return null;
    }
}

internal static AccountProfile NewUser
{
    get { return System.Web.HttpContext.Current.Profile as AccountProfile; }
}

新用户创建:

MembershipUser user = Membership.CreateUser(userName, password);
Roles.AddUserToRole(userName, "MyBasicUserRole");
AccountProfile.NewUser.Initialize(userName, true);
AccountProfile.NewUser.FullName = "Snoopy";
AccountProfile.NewUser.Save();

后续访问:

if (Membership.ValidateUser(userName, password))
{
    string name = AccountProfile.CurrentUser.FullName;
}

进一步感谢 Franci 解释身份验证生命周期 - 我在我的验证函数中调用 FormsAuthentication.SetAuthCookie ,但我返回一个布尔值表示成功,因为 User.Identity.IsAuthenticated 在后续请求之前不会为真。

修订:我是个白痴。上面的解释适用于狭义的情况,但并没有解决核心问题:调用 CurrentUser 每次都会返回一个对象的新实例,无论它是否是现有的 Profile。因为它被定义为一个属性,所以我没有考虑这个,并写道:

AccountProfile.CurrentUser.FullName = "Snoopy";
AccountProfile.CurrentUser.OtherProperty = "ABC";
AccountProfile.CurrentUser.Save();

这(当然)不起作用。它应该是:

AccountProfile currentProfile = AccountProfile.CurrentUser;
currentProfile.FullName = "Snoopy";
currentProfile.OtherProperty = "ABC";
currentProfile.Save();

完全忽略这个基本点是我自己的错,但我确实认为将 CurrentUser 声明为属性意味着它是一个可以操作的对象。相反,它应该被声明为GetCurrentUser().

4

3 回答 3

7

创建用户只是将其添加到用户列表中。但是,这不会对当前请求的新用户进行身份验证或授权。您还需要在当前请求上下文或后续请求中对用户进行身份验证。

Membership.ValidateUser只会验证凭据,但不会针对当前或后续请求对用户进行身份验证。FormsAuthentication.SetAuthCookie会在响应流中设置认证票据,所以下一个请求会被认证,但不影响当前请求的状态。

对用户进行身份验证的最简单方法是调用FormsAuthentication.RedirectFromLoginPage(假设您在应用程序中使用表单身份验证)。但是,这实际上会导致一个新的 HTTP 请求,该请求将对用户进行身份验证。

或者,如果您需要继续处理当前请求的逻辑,但希望对用户进行身份验证,您可以创建一个GenericPrincipal,为其分配新用户的身份并将 设置HttpContext.User为该主体。

于 2010-03-30T18:01:02.753 回答
1

如果启用匿名识别,您将遇到这种方法的问题。我建议使用 HttpContext.Profile.UserName 而不是 Membership.GetUser().UserName。

像这样...

private UserProfile _profile;
private UserProfile Profile
{
    get { return _profile ?? (_profile = (UserProfile)ProfileBase.Create(HttpContext.Profile.UserName)); }
}

提示:SqlProfileProvider - 你可以在项目中使用 Profile.GetProfile() 吗?

于 2012-01-21T21:27:32.397 回答
0

首先,感谢@Jeremy 分享您的发现。你帮助我朝着正确的方向前进。其次,很抱歉碰到这个旧帖子。希望这将帮助某人连接点。

我最终得到这个工作的方法是在我的配置文件类中使用以下静态方法:

internal static void InitializeNewMerchant(string username, Merchant merchant)
{
    var profile = System.Web.HttpContext.Current.Profile as MerchantProfile;
    profile.Initialize(username, true);
    profile.MerchantId = merchant.MerchantId;
    profile.Save();
}
于 2011-07-01T22:20:11.007 回答