将 ASP .NET 成员身份和配置文件与 MVC 结合使用,如何创建用户并将其设置为 HttpContext.Current.User?

发布于 2024-08-27 07:29:51 字数 3420 浏览 5 评论 0原文

我在代码中实现了一个自定义 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) 并以这种方式设置配置文件属性,但设置的属性仍为空,并调用 AccountProfile.CurrentUser(userName).Save() 不会将任何内容放入数据库中。我也尝试过表明用户是有效的&通过调用Membership.ValidateUserFormsAuthentication.SetAuthCookie等登录,但当前用户仍然为空或匿名(取决于我的浏览器cookie的状态)。

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

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

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

我的新 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,但我返回一个 bool 来指示成功,因为 User.Identity .IsAuthenticated 在后续请求之前不会为 true。

修订:我是个白痴。上面的解释适用于狭隘的情况,但没有解决核心问题:调用 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()

I implemented a custom Profile object in code as described by Joel here:

How to assign Profile values?

I can't get it to work when I'm creating a new user, however. When I do this:

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

the user is created and added to a role in the database, but HttpContext.Current.User is still empty, and Membership.GetUser() returns null, so this (from Joel's code) doesn't work:

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

AccountProfile.CurrentUser.FullName = "Snoopy";

I've tried calling Membership.GetUser(userName) and setting Profile properties that way, but the set properties remain empty, and calling AccountProfile.CurrentUser(userName).Save() doesn't put anything in the database. I've also tried indicating that the user is valid & logged in, by calling Membership.ValidateUser, FormsAuthentication.SetAuthCookie, etc., but the current user is still null or anonymous (depending on the state of my browser cookies).

SOLVED (EDITED FURTHER, SEE BELOW): Based on Franci Penov's explanation and some more experimentation, I figured out the issue. Joel's code and the variations I tried will only work with an existing Profile. If no Profile exists, ProfileBase.Create(userName) will return a new empty object every time it's called; you can set properties, but they won't "stick" because a new instance is returned every time you access it. Setting HttpContext.Current.User to a new GenericPrincipal will give you a User object, but not a Profile object, and ProfileBase.Create(userName) and HttpContext.Current.Profile will still point to new, empty objects.

If you want to create a Profile for a newly-created User in the same request, you need to call HttpContext.Current.Profile.Initialize(userName, true). You can then populate the initialized profile and save it, and it will be accessible on future requests by name, so Joel's code will work. I am only using HttpContext.Current.Profile internally, when I need to create/access the Profile immediately upon creation. On any other requests, I use ProfileBase.Create(userName), and I've exposed only that version as public.

Note that Franci is correct: If you are willing to create the User (and Roles) and set it as Authenticated on the first round-trip, and ask the user to then log in, you will be able to access the Profile much more simply via Joel's code on the subsequent request. What threw me is that Roles is immediately accessible upon user creation without any initialization, but Profile is not.

My new AccountProfile code:

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

New user creation:

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

Subsequent access:

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

Further thanks to Franci for explaining the Authentication life cycle - I'm calling FormsAuthentication.SetAuthCookie in my validation function, but I'm returning a bool to indicate success, because User.Identity.IsAuthenticated will not be true until the subsequent request.

REVISED: I'm an idiot. The above explanation works in the narrow case, but doesn't resolve the core problem: Calling CurrentUser returns a new instance of the object each time, whether it's an existing Profile or not. Because it's defined as a property, I wasn't thinking about this, and wrote:

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

which (of course) doesn't work. It should be:

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

It's my own fault for completely overlooking this basic point, but I do think declaring CurrentUser as a property implies that it's an object that can be manipulated. Instead, it should be declared as GetCurrentUser().

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

苄①跕圉湢 2024-09-03 07:29:52

首先,感谢@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();
}

First of all, thanks @Jeremy for sharing your findings. You helped me get going in the right direction. Secondly, sorry for bumping this old post. Hopefully this will help someone connect the dots.

The way I finally got this working was to use the following static method inside my profile class:

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();
}
不一样的天空 2024-09-03 07:29:51

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

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

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

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

Creating a user just adds it to the list of users. However, this does not authenticate or authorize the new user for the current request. You also need to authenticate the user in the current request context or for subsequent requests.

Membership.ValidateUser will only validate the credentials, but it's not authenticating the user for the current or subsequent requests. FormsAuthentication.SetAuthCookie will set the authentication ticket in the response stream, so the next request will be authenticated, but it does not affect the state of the current request.

The easiest way to authenticate the user would be to call FormsAuthentication.RedirectFromLoginPage (assuming you are using forms authentication in your app). However, this one would actually cause a new HTTP request, which will authenticate the user.

Alternatively, if you need to continue your logic for processing the current request, but want the user to be authenticated, you can create a GenericPrincipal, assign it the identity of the new user and set the HttpContext.User to that principal.

铃予 2024-09-03 07:29:51

如果启用匿名识别,您将遇到此方法的问题。我建议使用 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()?

You are going to run into problems with this approach if you enable anonymousIdentification. Rather than Membership.GetUser().UserName, I would suggest using HttpContext.Profile.UserName.

Like this...

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

Hat tip: SqlProfileProvider - can you use Profile.GetProfile() in a project?

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文