2

我网站的用户昨天遇到了一些奇怪的行为(我第一次看到这个问题),不幸的是我没有太多的错误日志来试图弄清楚发生了什么。该站点同时在线的人数高于正常数量,尽管在总体规划中并不多(可能有 50 到 100 个用户都在尝试执行类似的功能)。我无法在我的开发环境中重现该问题,以前从未见过,也不知道为什么会发生。

问题的症结在于,用户可以注册或登录成功,但少数用户可以看到其他用户的数据。

该站点是 ASP.NET MVC 3。

用户正在登录,我设置了身份验证 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 是一个简单的对象,具有两个字符串属性,用户名和密码。

据我所知,这很好 - 如果您以 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;
    }

所以我试图从我的数据库中检索一个用户对象,并使用它来创建一个新的 MembershipUser 对象。我的数据库用户表在会员资格提供商要求的列之上还有其他列 - 例如姓名、地址、电话号码等。

在网站其余部分的各个点(例如,如果您转到个人资料页面),我从数据库中检索一个用户对象并使用它来填充屏幕。我用来检索用户对象的行是:

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

所以在我看来,好像我设置的 auth cookie 很好,但是:当我第一次进入会员提供者的 GetUser() 方法时,它从数据库中检索错误的记录,因此设置了一个 MembershipUser 对象错误的用户名;随后,当我在数据库中查找“此”用户时,我实际上是在寻找错误的用户名。

或者:当我执行 userRepository.FirstOrDefault(x => x.UserName == Membership.GetUser().Name) 时,它会间歇性地检索错误的记录。

或者:我没有想到的其他事情出错了。

正如我所说,当网站处于负载状态时,这似乎是一个问题,所以我想知道这是否是某个地方的某种缓存问题?但我真的不知道。

我的一个想法是改变我检索用户的方式,以防问题出在成员资格提供者上,并改用它:

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

但实际上我什至不确定发生了什么,所以不知道这是否会解决问题。会不会是某个地方的缓存问题?看起来(但我不能 100% 确定)当用户 A 可以看到用户 B 的详细信息时,用户 B 总是在系统中处于活动状态(或者在前 20 分钟内处于活动状态)。

我知道这是一个很长的镜头,但有人知道这是怎么发生的吗?显然这是一个主要问题,需要紧急修复,但不知道为什么会发生,我无法修复它!

在此先感谢您的帮助,尼克

4

1 回答 1

2

需要考虑的一些事项:

  1. 而不是使用FirstOrDefault,使用SingleOrDefaultFirstOrDefault假设将有超过 1 条与您的查询匹配的数据记录。由于您是按用户名查询,因此应该只有 1 个匹配行,对吗?在这种情况下,请SingleOrDefault改用。当有多行匹配查询时,SingleOrDefault会抛出异常。

  2. 要获取用户名,而不是调用Membership.GetUser().UserName,请使用User.Identity.Name. MVC 控制器上的User属性引用IPrincipal应该与用户的表单身份验证 cookie 值匹配的值。由于您有一个自定义成员资格提供程序,这应该有助于消除其方法作为问题的根源。

  3. 如果您为 MVC 项目设置了缓存,则可能存在缓存问题。您是否在任何控制器或操作方法上使用OutputCacheAttribute( )?[OutputCache]您是否将其设置为 global.asax 文件中的全局过滤器?或者您认为可能会发生某种基于 SQL 的缓存?

  4. 查看您覆盖的GetUser方法,我认为它应该采用 2 个参数:string usernamebool isOnline. 但是,当您使用 调用它时Membership.GetUser().UserName,您没有传递任何参数。您是否有此方法的另一个不带参数的重写重载?它是什么样子的?当没有通过时,它是否System.Threading.CurrentPrincipal.Identity.Name用于嗅出当前用户名?

于 2012-12-31T14:07:19.443 回答