Сколько из MembershipProvider мне * нужно * переопределить в MVC3 - PullRequest
3 голосов
/ 27 января 2012

До сих пор я выполнял всю работу по аутентификации в своем приложении MVC3, то есть проверял члена и создавал члена через мой класс MemberRepository. Я бы сейчас хотел стать официальным, с кастомом MembershipProvider. До сих пор я только что понял, что мне действительно нужно переопределить метод ValidateUser этого класса, и, поскольку я не использую элемент управления Login, я даже не уверен, что мне абсолютно необходимо это сделать.

Переопределяющие методы, такие как GetUser и CreateUser, привносят в мою группу незваные типы, такие как MembershipUser, где у меня есть класс Member. Пожалуйста, может кто-нибудь уточнить для меня, действительно ли мне нужен специальный поставщик членства, если я не собираюсь использовать какие-либо встроенные элементы управления или инструмент администратора, и если я это сделаю, должен ли я ограничить свои переопределения абсолютно необходимыми что к чему?

Ответы [ 3 ]

2 голосов
/ 27 января 2012

Вот один, который я написал для модульного тестирования. Это как можно меньше.

public class MockMembershipProvider : MembershipProvider
{
    public IList<MembershipUser> Users { get; private set; }

    private string _applicationName;
    public override string ApplicationName
    {
        get
        {
            return _applicationName;
        }
        set
        {
            _applicationName = value;
        }
    }

    public override bool ChangePassword(string username, string oldPassword, string newPassword)
    {
        throw new NotImplementedException();
    }

    public override bool ChangePasswordQuestionAndAnswer(string username, string password, string newPasswordQuestion, string newPasswordAnswer)
    {
        throw new NotImplementedException();
    }

    public override MembershipUser CreateUser(
        string username, 
        string password, 
        string email, 
        string passwordQuestion, 
        string passwordAnswer, 
        bool isApproved, 
        object providerUserKey, 
        out MembershipCreateStatus status)
    {
        var user = new MembershipUser(ProviderName, username, username, email, passwordQuestion, null, isApproved, false, DateTime.Now, DateTime.Now, DateTime.Now, DateTime.Now, DateTime.Now);
        Users.Add(user);
        status = MembershipCreateStatus.Success;
        return user;
    }

    public override bool DeleteUser(string username, bool deleteAllRelatedData)
    {
        var u = Users.Where(mu => mu.UserName.Equals(username, StringComparison.OrdinalIgnoreCase)).FirstOrDefault();
        if (u == null) return false;
        Users.Remove(u);
        return true;
    }

    public override bool EnablePasswordReset
    {
        get { return false; }
    }

    public override bool EnablePasswordRetrieval
    {
        get { return false; }
    }

    public override MembershipUserCollection FindUsersByEmail(string emailToMatch, int pageIndex, int pageSize, out int totalRecords)
    {
        throw new NotImplementedException();
    }

    public override MembershipUserCollection FindUsersByName(string usernameToMatch, int pageIndex, int pageSize, out int totalRecords)
    {
        var users = (from u in Users
                     where u.UserName.Equals(usernameToMatch, StringComparison.OrdinalIgnoreCase)
                     select u).ToList();
        totalRecords = users.Count;
        return ToMembershipUserCollection(users);
    }

    public override MembershipUserCollection GetAllUsers(int pageIndex, int pageSize, out int totalRecords)
    {
        var list = Users.Skip(pageIndex * pageSize).Take(pageSize);
        totalRecords = list.Count();
        var result = new MembershipUserCollection();
        foreach (var u in list)
        {
            result.Add(u);
        }
        return result;
    }

    public override int GetNumberOfUsersOnline()
    {
        return Users.Count();
    }

    public override string GetPassword(string username, string answer)
    {
        throw new NotImplementedException();
    }

    public override MembershipUser GetUser(object providerUserKey, bool userIsOnline)
    {
        return (from u in Users
                where u.ProviderUserKey.ToString() == providerUserKey.ToString()
                select u).FirstOrDefault();
    }

    public override MembershipUser GetUser(string username, bool userIsOnline)
    {
        return (from u in Users
                where u.UserName.Equals(username, StringComparison.OrdinalIgnoreCase)
                select u).FirstOrDefault();
    }

    public override string GetUserNameByEmail(string email)
    {
        return (from u in Users
                where u.Email.Equals(email, StringComparison.OrdinalIgnoreCase)
                select u.UserName).FirstOrDefault();
    }

    public override int MaxInvalidPasswordAttempts
    {
        get { return 3; }
    }

    public override int MinRequiredNonAlphanumericCharacters
    {
        get { return 1; }
    }

    public override int MinRequiredPasswordLength
    {
        get { return 6; }
    }

    public override int PasswordAttemptWindow
    {
        get { return 10; }
    }

    public override MembershipPasswordFormat PasswordFormat
    {
        get { throw new NotImplementedException(); }
    }

    public override string PasswordStrengthRegularExpression
    {
        get { return null; }
    }

    public override string Name
    {
        get
        {
            return ProviderName;
        }
    }

    public string ProviderName { get; set; }

    public override string ResetPassword(string username, string answer)
    {
        throw new NotImplementedException();
    }

    public override bool RequiresQuestionAndAnswer
    {
        get { return false; }
    }

    public override bool RequiresUniqueEmail
    {
        get { return true; }
    }

    private MembershipUserCollection ToMembershipUserCollection(IEnumerable<MembershipUser> users)
    {
        var result = new MembershipUserCollection();
        foreach (var u in users)
        {
            result.Add(u);
        }
        return result;
    }

    public override bool UnlockUser(string userName)
    {
        return true;
    }

    public override void UpdateUser(MembershipUser user)
    {
        var oldUser = Users.Where(u => u.UserName.Equals(user.UserName, StringComparison.OrdinalIgnoreCase)).Single();
        var index = Users.IndexOf(oldUser);
        Users[index] = user;
    }

    public override bool ValidateUser(string username, string password)
    {
        throw new NotImplementedException();
    }

    public MockMembershipProvider()
    {
        this.ProviderName = "MockMembershipProvider";
        Users = new List<MembershipUser>();
    }
}

public class FakeMembershipProvider : MockMembershipProvider
{
    public FakeMembershipProvider(string name)
    {
        this.ProviderName = name ?? "MockMembershipProvider";
    }
    public override MembershipUser CreateUser(
        string username,
        string password,
        string email,
        string passwordQuestion,
        string passwordAnswer,
        bool isApproved,
        object providerUserKey,
        out MembershipCreateStatus status)
    {
        status = MembershipCreateStatus.ProviderError;
        var user = new MockMembershipUser(); 
        user.Password = password;
        user.User = username;
        user.UserKey = providerUserKey;
        Users.Add(user);
        status = MembershipCreateStatus.Success;
        return user;
    }
}

public class MockMembershipUser : MembershipUser
{
    public string Password { get; set; }
    public string User { get; set; }
    public object UserKey { get; set; }

    public override string UserName { get { return User; } }

    public override string Comment { get; set; }

    public override object ProviderUserKey { get { return UserKey; } }

    public override string GetPassword()
    {
        return Password ?? string.Empty;
    }
1 голос
/ 28 января 2012

Custom MembershipProvider

Можно получить некоторые полезные функции безопасности "бесплатно", если вы используете MembershipProvider: например, вы можете настроить web.config для перенаправления каждого неаутентифицированного пользователя на страницу входа. Или вы можете настроить определенные части сайта, чтобы они были видны только пользователям с определенными ролями. Если эти функции не имеют смысла для вашего проекта или вы уже реализуете их эквивалент другим способом, нет особого смысла в реализации пользовательского MembershipProvider.

SqlMembershipProvider

Еще одна возможность, которую вы можете рассмотреть, - это на самом деле переключение собственной реализации на использование SqlMembershipProvider для обработки функций членства.

SqlMembershipProvider предоставляет надежную, проверенную платформу для общих задач, которые надоело заново изобретать для каждого проекта: создание учетной записи, проверка, удаление, блокировка, сброс пароля, основные роли и т. Д. Если вы уже сделали все этого без с использованием SqlMembershipProvider, на самом деле нет никакого смысла создавать его только ради его наличия. Однако вы должны быть осторожны, потому что есть большая вероятность, что вы сделали что-то не так в своей собственной реализации. Например,

  • Вы храните пароли в виде простого текста или в виде хэшей?
  • Вы открыты для Радужного стола атак или вы солите свои хэши?
  • Вы блокируете учетные записи людей после того, как вы видели 50 или около того попыток ввода неверных паролей подряд, или вы позволяете хакерам просто продолжать набивать, пока они грубо не пробились в чей-то аккаунт?

SqlMembershipProvider уже решил все эти проблемы в легко настраиваемой форме. Возможно, вы захотите иметь свои собственные интерфейсы членства, и DTO просто обернут этот членство по умолчанию MembershipProvider, чтобы вам не приходилось беспокоиться об этих различных проблемах. Таким образом, большая часть вашего кода не должна взаимодействовать с этими «незванными типами», но вы по-прежнему получаете преимущества широко используемой и проверенной инфраструктуры безопасности на серверной части.

1 голос
/ 27 января 2012

Хотите отделить свое веб-приложение от MembershipRepository?

Если это так, реализуйте все те же функциональные возможности в пользовательском MembershipProvider , чтобы ваше приложение зависело только от .NET классов членства (кроме вашей сети).config).

Если нет, то не беспокойтесь.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...