2

私のサイトのユーザーは昨日(この問題を初めて見たとき)奇妙な振る舞いを経験しました、そして残念ながら私は何が起こっているのかを理解しようとするエラーログの邪魔をすることはあまりありません。このサイトには、大規模な計画では多くはありませんが、一度にオンラインで通常よりも多くの人がいました(おそらく、50〜100人のユーザーがすべて同様の機能を実行しようとしています)。私は自分の開発環境で問題を再現することができず、以前にそれを見たことがなく、なぜそれが起こっているのか本当にわかりません。

問題の核心は、ユーザーが正常に登録またはログオンできることですが、少数のユーザーが他のユーザーのデータを見る可能性があります。

サイトはASP.NETMVC3です。

ユーザーがログオンしていて、認証Cookieを設定しました-ログオンアクションは次のとおりです。

    [HttpPost]
    public ActionResult LogOn(AccountLogOnViewModel model, string returnUrl)
    {
        if (ModelState.IsValid)
        {
            if (!Membership.ValidateUser(model.UserName, model.Password))
            {
                ModelState.AddModelError("login-message", "Incorrect username or password");
            }
        }

        if (ModelState.IsValid)
        {
            FormsAuthentication.SetAuthCookie(model.UserName, model.RememberMe);
            Session.Remove("MenuItems");
            return Redirect(returnUrl ?? Url.Action("Index", "Home"));
        }
        else
        {
            model.ReturnUrl = returnUrl;
            return View(model);
        }
    }

AccountLogOnViewModelは、UserNameとPasswordの2つの文字列プロパティを持つ単純なオブジェクトです。

私が収集できることから、これは問題ありません。NickWとしてログインし、User.Identity.Nameのような操作を行うと、「NickW」が正しく表示されます(ユーザーが他のユーザーのデータを表示しているときに、「ようこそ、NickW」と報告されました。画面上のテキストは正しい値を示していました-これはUser.Identity.Nameを使用して書き出されます)

このサイトでは、カスタムメンバーシッププロバイダーも使用しています。これは、ValidateLoginメソッドとGetUserメソッドをオーバーライドします。ValidateLoginは正常に機能しているように見えるので、心配していません。

オーバーライドされるGetUserメソッドは次のとおりです。

    public override MembershipUser GetUser(string username, bool userIsOnline)
    {
        User user = _userRepository.Users.FirstOrDefault(u => u.UserName == username);
        MembershipUser membershipUser = null;

        if (user == null)
            return membershipUser;

        membershipUser = new MembershipUser(this.Name,
            user.UserName,
            user.Id,
            user.Email,
            null,
            user.Comments,
            user.IsActivated,
            user.IsLockedOut,
            user.CreatedDate,
            user.LastLoginDate,
            user.LastLoginDate,
            user.LastModifiedDate,
            Convert.ToDateTime(user.LastLockedOutDate));

        return membershipUser;
    }

そのため、データベースからUserオブジェクトを取得し、それを使用して新しいMembershipUserオブジェクトを作成しようとしています。私のデータベースのユーザーテーブルには、メンバーシッププロバイダーが必要とする列に加えて、名前、住所、電話番号などの追加の列があります。

Webサイトの残りのさまざまなポイント(たとえば、プロファイルページに移動した場合)で、データベースからユーザーオブジェクトを取得し、それを使用して画面にデータを入力します。Userオブジェクトを取得するために使用する行は次のとおりです。

User user = userRepository.Users.FirstOrDefault(u => u.UserName == Membership.GetUser().UserName);

これは、userRepositoryの縮小版です(つまり、無関係なコードを削除するだけです)。

public class SqlUserRepository : IUserRepository
{
    private Table<User> usersTable;
    private string _connectionString;

    public SqlUserRepository(string connectionString)
    {
        _connectionString = connectionString;
        usersTable = (new DataContext(connectionString)).GetTable<User>();
    }


    public IQueryable<User> Users
    {
        get { return usersTable; }
    }

    public void CreateUser(AccountRegisterViewModel user)
    {
        User newUser = new User();

        newUser.UserName = user.UserName;
        newUser.Salutation = user.Salutation;
        newUser.PhoneNumber = user.PhoneNumber;
        newUser.SecondaryPhoneNumber = user.SecondaryPhoneNumber;
        newUser.FirstName = user.FirstName;
        newUser.LastName = user.LastName;
        newUser.PasswordSalt = CreateSalt();
        newUser.Password = CreatePasswordHash(user.Password, newUser.PasswordSalt);
        newUser.Email = user.Email;
        newUser.CreatedDate = DateTime.UtcNow;
        newUser.Comments = "Created from web registration";
        newUser.LastModifiedDate = DateTime.UtcNow;
        newUser.LastLoginDate = DateTime.UtcNow;
        newUser.IsActivated = true;
        newUser.IsLockedOut = false;
        newUser.MayContact = user.MayContact;

        usersTable.InsertOnSubmit(newUser);
        usersTable.Context.SubmitChanges();
    }
}

したがって、設定している認証Cookieは問題ないように見えますが、次のいずれかです。メンバーシッププロバイダーのGetUser()メソッドに最初にアクセスすると、データベースから間違ったレコードが取得されるため、MembershipUserオブジェクトが次のように設定されます。間違ったユーザー名。その後、データベースで「この」ユーザーを探すとき、実際には間違ったユーザー名を探しています。

または:userRepository.FirstOrDefault(x => x.UserName == Membership.GetUser()。Name)を断続的に実行すると、間違ったレコードが取得されます。

または:私が考えていなかった何か他の問題が発生しています。

私が言っているように、これはサイトに負荷がかかっているときに問題になるようです。それで、どこかで何らかのキャッシュの問題があるのではないかと思います。しかし、私は本当に知りません。

私が持っていた1つの考えは、問題がメンバーシッププロバイダーにある場合に備えて、ユーザーを取得する方法を変更し、代わりにこれを使用することでした。

userRepository.FirstOrDefault(x => x.UserName == User.Identity.Name)
// or HttpContext.Current.User.Identity.Name if not within a controller

しかし、実際には何が起こっているのかさえわからないので、これで問題が解決するかどうかはわかりません。どこかでキャッシュの問題でしょうか?ユーザーAがユーザーBの詳細を見ることができたとき、ユーザーBもシステムでアクティブであった(または過去20分以内にいた)ことが常にあったように見えます(ただし、100%確実ではありません)。

私はそれがロングショットであることを知っています、しかし誰かがこれがどのように起こることができるかについて何か考えを持っていますか?明らかにそれは大きな懸念事項であり、緊急に修正する必要がありますが、なぜそれが起こっているのかわからなければ、私はそれを修正することはできません!

助けてくれてありがとう、ニック

4

1 に答える 1

2

考慮すべき事項:

  1. を使用する代わりに、 をFirstOrDefault使用してSingleOrDefaultください。FirstOrDefaultクエリに一致するデータのレコードが複数あることを前提としています。ユーザー名でクエリを実行しているため、一致する行は 1 つだけですよね? その場合は、SingleOrDefault代わりに使用してください。クエリに一致する行が複数ある場合はSingleOrDefault、例外がスローされます。

  2. ユーザー名を取得するには、 を呼び出す代わりに をMembership.GetUser().UserName使用しますUser.Identity.NameUserMVC コントローラーのプロパティはIPrincipal、ユーザーのフォーム認証 Cookie 値と一致する必要がある を参照します。カスタム メンバーシップ プロバイダーがあるため、これは問題の原因となっているメソッドを排除するのに役立ちます。

  3. MVC プロジェクトにキャッシュが設定されている場合、キャッシュの問題が発生する可能性があります。コントローラーまたはアクション メソッドでOutputCacheAttribute( )を使用しますか? [OutputCache]global.asax ファイルでグローバル フィルターとして設定していますか? それとも、何らかの SQL ベースのキャッシングが行われていると思いますか?

  4. オーバーライドされたメソッドを見ると、とのGetUser2 つのパラメーターが必要であることがわかります。ただし、で呼び出すと、パラメーターは渡されません。パラメータをとらない、このメソッドの別のオーバーライドされたオーバーロードがありますか? それはどのように見えますか?何も渡されない場合、現在のユーザー名を盗聴するために使用しますか?string usernamebool isOnlineMembership.GetUser().UserNameSystem.Threading.CurrentPrincipal.Identity.Name

于 2012-12-31T14:07:19.443 に答える