开发者

How to initialize information on authorization [closed]

开发者 https://www.devze.com 2023-04-01 06:05 出处:网络
Closed. This question is opinion-based. It is not currently accepting answers. Want to improve this question? Update the question so it can be answered with facts and citations by editing th
Closed. This question is opinion-based. It is not currently accepting answers.

Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.

Closed 4 years ago.

Improve this question

Ok, so this seems like a common need. A little googling finds a lot of ways to do this. I'm interested in the most "mvc correct" way to do it.

I have, in the upper right hand corner of my app, a greeting that says Hello FirstName LastName. Now, it's quite easy to get at the username of the logged in user, through the IPrincipal (aka User.Identity.Name). However, this won't give me the First and Last name of the user. I have to hit the Membership API to get that.

Hitting the Membership API has its drawbacks. It hits the database every time, which adds an additional db access to every served page. It's easy enough to set some session variables on login, but this only works for that session. If the user clicks the "Remember me", then no login occurs next time and i have to still load these values.

  1. I could create my own membership provider to do some cacheing, but that's a lot of work for a more or less single purpose.
  2. I could use Application_AuthenticateRequest and hit the membership api and store the values in session variables, or something similar. This is ok, but seems a little brute force.
  3. I could register a global filter and handle OnAuthenticate, essentially doing the same thing. This seems a little better, but i'm unusre of the ramifications here.
  4. I could derive a base controller, and simly add properties to provide this information. This seems a bit "old school", and I hate having to make a base class for a single purpose.
  5. I could create a cacheing static method that would get the information on first access. This is basically not much better than a singleton.
  6. I could also create my own IPrincipal, but that means casting it every time to get at the data, and that seems clunky. I could wrap that in another class to simplify it, but still...
  7. I could store the data in the forms authentication cookie, and get it from there. There's开发者_运维问答 some tools available to make that easier.

Are there any methods I haven't thought of? And what is the most "mvc correct" way of doing it?


I think the best way is using Cookies. Here is the solution I used in my project:

Create a class to save data in it

[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 method:

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

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

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

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


The FormsAuthenticationExtensions project solves this problem storing the additional information in the auth cookie itself. http://formsauthext.codeplex.com/

This spares the database hit, and lives as long as the auth cookie, hence it works if the users asks for "remember me". It can be used the same way (in MVC too) as the standard forms authentication.

To your question, what is the most MVCisch way: I would first decide where I want to keep the information. This part of the question is rather independent of the MVC framework as the concepts (session, post data, cookies, etc.) are given with or without it.


I will implement and extend the IPrincipal and IIdentity, so when you access User.Identity you will find LastName and FirstName. This way is better imo.

For my projects I have extended IIdentity and IPrincipal with my classes adding the properties I always need "to be there". To me is not this big work, I mean, there are only a bunch of methods that need to be implemented.

For IIdentity the interface requirement are only AuthenticationType (string), IsAuthenticated (bool) and Name (string).

While in IPrincipal Identity (IIDentity) and IsInRole (boolean)

0

精彩评论

暂无评论...
验证码 换一张
取 消