Как инициализировать информацию о авторизации

Хорошо, так что это кажется общей потребностью. Маленький поисковик находит много способов сделать это. Меня интересует самый правильный способ "mvc correct".

В верхнем правом углу моего приложения есть приветствие, в котором говорится Hello FirstName LastName. Теперь довольно легко получить имя пользователя зарегистрированного пользователя через IPrincipal (aka User.Identity.Name). Однако это не даст мне первое и последнее имя пользователя. Я должен ударить по API членства, чтобы получить это.

Уступка API членства имеет свои недостатки. Он попадает в базу данных каждый раз, что добавляет дополнительный доступ к каждой доступной странице. Достаточно легко установить некоторые переменные сеанса при входе в систему, но это работает только для этого сеанса. Если пользователь нажимает кнопку "Запомнить меня", то в следующий раз вход в систему не будет выполняться, и мне придется загрузить эти значения.

  • Я мог бы создать свой собственный членский провайдер, чтобы сделать некоторое кэширование, но это большая работа для более или менее единой цели.
  • Я мог бы использовать Application_AuthenticateRequest и ударить членство api и сохранить значения в переменных сеанса или что-то подобное. Это нормально, но кажется немного грубой силой.
  • Я могу зарегистрировать глобальный фильтр и обработать OnAuthenticate, по существу делая то же самое. Это кажется немного лучше, но я не знаю, как это повлияет.
  • Я мог бы получить базовый контроллер и просто добавить свойства для предоставления этой информации. Это кажется немного "старой школой", и мне не нравится создавать базовый класс для одной цели.
  • Я могу создать статический метод кэширования, который получит информацию о первом доступе. Это в основном не намного лучше, чем синглтон.
  • Я мог бы также создать свой собственный IPrincipal, но это означает, что каждый раз бросать его на данные, и это кажется неуклюжим. Я мог бы обернуть это в другом классе, чтобы упростить его, но все же...
  • Я могу хранить данные в cookie проверки подлинности форм и получать их оттуда. Там есть некоторые инструменты, чтобы сделать это проще.

Есть ли какие-то методы, о которых я не думал? И каков самый "правильный mvc" способ сделать это?

Ответ 1

Я думаю, что лучший способ - использовать Cookies. Вот решение, которое я использовал в моем проекте:

Создать класс для сохранения данных в нем

[DataContract]
[Serializable()]
public class AuthData {

    [DataMember]
    public String UserName { get; set; }

    [DataMember]
    public String FirstName { get; set; }

    [DataMember]
    public String LastName { get; set; }

    [DataMember]
    public String Email { get; set; }

    // any other property you need to a light-store for each user

    public override string ToString() {
        string result = "";
        try {
            using (MemoryStream stream = new MemoryStream()) {
                BinaryFormatter formatter = new BinaryFormatter();
                formatter.Serialize(stream, this);
                result = Convert.ToBase64String(stream.ToArray());
            }
        } catch (Exception ex) {
            throw new HttpException(ex.Message);
        }
        return result;
    }

    static public AuthData FromString(String data) {
        AuthData result = null;
        try {
            byte[] array = Convert.FromBase64String(data);
            using (MemoryStream stream = new MemoryStream(array)) {
                stream.Seek(0, 0);
                BinaryFormatter formatter = new BinaryFormatter();
                result = (AuthData)formatter.Deserialize(stream, null);
            }
        } catch (Exception ex) {
            throw new HttpException(ex.Message);
        }
        return result;
    }
}

Метод Signin:

public static bool SignIn(string userName, string password, bool persistent){
    if (Membership.ValidateUser(userName, password)) {
        SetAuthCookie(userName, persistent);
        return true;
    }
    return false;
}

Настройка AuthCookie:

public static void SetAuthCookie(string userName, bool persistent) {
    AuthData data = GetAuthDataFromDB(); // implement this method to retrieve data from database as an AuthData object
    var ticket = new FormsAuthenticationTicket(
        1, 
        userName, 
        DateTime.Now,
        DateTime.Now.Add(FormsAuthentication.Timeout), 
        persistent, 
        data.ToString(), 
        FormsAuthentication.FormsCookiePath
        );
    string hash = FormsAuthentication.Encrypt(ticket);
    HttpCookie cookie = new HttpCookie(FormsAuthentication.FormsCookieName, hash);
    cookie.Expires = DateTime.Now.Add(FormsAuthentication.Timeout);
    cookie.HttpOnly = false;
    cookie.Path = FormsAuthentication.FormsCookiePath;
    HttpContext.Current.Response.Cookies.Add(cookie);
}

Получение AuthCookie:

public static AuthData GetAuthCookie() {
    if (HttpContext.Current.User != null && HttpContext.Current.User.Identity.IsAuthenticated && HttpContext.Current.User.Identity is FormsIdentity) {
        FormsIdentity id = (FormsIdentity)HttpContext.Current.User.Identity;
        FormsAuthenticationTicket ticket = id.Ticket;
        var data = AuthData.FromString(ticket.UserData);
        HttpContext.Current.Items["AuthDataContext"] = data;
        return data;
    }
    return null;
}

В ControllerBase:

private AuthData _authData;
private bool _authDataIsChecked;
public AuthData AuthData {
    get {
        _authData = System.Web.HttpContext.Current.Items["AuthDataContext"] as AuthData;
        if (!_authDataIsChecked && _authData == null) {
            SignService.GetAuthCookie();
            _authData = System.Web.HttpContext.Current.Items["AuthDataContext"] as AuthData;
            _authDataIsChecked = true;
        }
        return _authData;
    }
}

Ответ 2

Проект FormsAuthenticationExtensions решает эту проблему, сохраняя дополнительную информацию в самом файле cookie. http://formsauthext.codeplex.com/

Это избавляет базу данных от попадания в нее и живет до тех пор, пока файл cookie auth, следовательно, работает, если пользователи попросят "запомнить меня". Его можно использовать одинаково (в MVC тоже) в качестве стандартной проверки подлинности.

На ваш вопрос, что является самым MVCisch образом: я бы сначала решить, где я хочу сохранить информацию. Эта часть вопроса довольно независима от структуры MVC, поскольку понятия (сеанс, почтовые данные, файлы cookie и т.д.) Даются с или без него.

Ответ 3

Я буду реализовывать и расширять IPrincipal и IIdentity, поэтому при доступе к User.Identity вы найдете LastName и FirstName. Этот способ лучше imo.

Для моих проектов я расширил IIdentity и IPrincipal с помощью своих классов, добавляя свойства, которые мне всегда нужны "быть там". Для меня это не большая работа, я имею в виду, что есть только несколько методов, которые необходимо реализовать.

Для IIdentity требования к интерфейсу - это только AuthenticationType (string), IsAuthenticated (bool) и Name (string).

В режиме IPrincipal Identity (IIDentity) и IsInRole (boolean)