ASP.NET MVC - Ustaw niestandardową IIdentity lub IPrincipal

650

Muszę zrobić coś dość prostego: w mojej aplikacji ASP.NET MVC chcę ustawić niestandardową IIdentity / IPrincipal. Którykolwiek jest łatwiejszy / bardziej odpowiedni. Chcę rozszerzyć wartość domyślną, aby móc wywoływać coś takiego jak User.Identity.Idi User.Identity.Role. Nic szczególnego, tylko dodatkowe właściwości.

Przeczytałem mnóstwo artykułów i pytań, ale wydaje mi się, że robię to trudniejszym niż jest w rzeczywistości. Myślałem, że będzie łatwo. Jeśli użytkownik się zaloguje, chcę ustawić niestandardową IIdentity. Pomyślałem więc, że zaimplementuję Application_PostAuthenticateRequestw moim global.asax. Jest to jednak wywoływane przy każdym żądaniu i nie chcę wykonywać wywołania do bazy danych przy każdym żądaniu, które zażądałoby wszystkich danych z bazy danych i umieściłoby niestandardowy obiekt IPrincipal. Wydaje się to również bardzo niepotrzebne, powolne i w niewłaściwym miejscu (wykonywanie tam wywołań bazy danych), ale mogę się mylić. Lub skąd jeszcze pochodzą te dane?

Pomyślałem więc, że za każdym razem, gdy użytkownik się loguje, mogę dodać niezbędne zmienne do mojej sesji, które dodam do niestandardowej IIdentity w module Application_PostAuthenticateRequestobsługi zdarzeń. Jednak moje tam Context.Sessionjest null, więc nie jest to również właściwa droga.

Pracuję nad tym przez jeden dzień i czuję, że coś mi umknęło. Nie powinno to być zbyt trudne, prawda? Jestem również trochę zdezorientowany wszystkimi (częściowo) powiązanymi z tym rzeczami. MembershipProvider, MembershipUser, RoleProvider, ProfileProvider, IPrincipal, IIdentity, FormsAuthentication.... ja jestem jedyną osobą, która wyszukuje wszystko to bardzo mylące?

Jeśli ktoś mógłby mi powiedzieć proste, eleganckie i wydajne rozwiązanie do przechowywania dodatkowych danych w IIdentity bez wszystkich dodatkowych problemów ... byłoby świetnie! Wiem, że na SO są podobne pytania, ale jeśli potrzebuję odpowiedzi, muszę to przeoczyć.

Razzie
źródło
1
Cześć Domi, to połączenie tylko przechowywania danych, które nigdy się nie zmieniają (np. ID użytkownika) lub aktualizacji pliku cookie bezpośrednio po zmianie danych, które muszą zostać odzwierciedlone w pliku cookie. Jeśli użytkownik to zrobi, po prostu aktualizuję plik cookie o nowe dane. Ale staram się nie przechowywać danych, które często się zmieniają.
Razzie
26
to pytanie ma 36 tys. wyświetleń i wiele pozytywnych opinii. czy to naprawdę tak powszechne wymaganie - a jeśli tak, to czy nie ma lepszego sposobu niż te wszystkie „niestandardowe rzeczy”?
Simon_Weaver
2
@Simon_Weaver Istnieje funkcja ASP.NET Identity, która ułatwia obsługę dodatkowych niestandardowych informacji w zaszyfrowanym pliku cookie.
John
1
Zgadzam się z tobą, tam jest dużo informacji jakbyś pisał: MemberShip..., Principal, Identity. ASP.NET powinien uprościć, uprościć i maksymalnie dwa podejścia do uwierzytelniania.
Internet szerokopasmowy
1
@Simon_Weaver To wyraźnie pokazuje, że istnieje zapotrzebowanie na prostszy i bardziej elastyczny system identyfikacji IMHO.
niico

Odpowiedzi:

838

Oto jak to robię.

Zdecydowałem się użyć IPrincipal zamiast IIdentity, ponieważ oznacza to, że nie muszę implementować zarówno IIdentity, jak i IPrincipal.

  1. Utwórz interfejs

    interface ICustomPrincipal : IPrincipal
    {
        int Id { get; set; }
        string FirstName { get; set; }
        string LastName { get; set; }
    }
  2. CustomPrincipal

    public class CustomPrincipal : ICustomPrincipal
    {
        public IIdentity Identity { get; private set; }
        public bool IsInRole(string role) { return false; }
    
        public CustomPrincipal(string email)
        {
            this.Identity = new GenericIdentity(email);
        }
    
        public int Id { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
    }
  3. CustomPrincipalSerializeModel - do szeregowania niestandardowych informacji w polu danych użytkownika w obiekcie FormsAuthenticationTicket.

    public class CustomPrincipalSerializeModel
    {
        public int Id { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
    }
  4. Metoda logowania - konfigurowanie pliku cookie z niestandardowymi informacjami

    if (Membership.ValidateUser(viewModel.Email, viewModel.Password))
    {
        var user = userRepository.Users.Where(u => u.Email == viewModel.Email).First();
    
        CustomPrincipalSerializeModel serializeModel = new CustomPrincipalSerializeModel();
        serializeModel.Id = user.Id;
        serializeModel.FirstName = user.FirstName;
        serializeModel.LastName = user.LastName;
    
        JavaScriptSerializer serializer = new JavaScriptSerializer();
    
        string userData = serializer.Serialize(serializeModel);
    
        FormsAuthenticationTicket authTicket = new FormsAuthenticationTicket(
                 1,
                 viewModel.Email,
                 DateTime.Now,
                 DateTime.Now.AddMinutes(15),
                 false,
                 userData);
    
        string encTicket = FormsAuthentication.Encrypt(authTicket);
        HttpCookie faCookie = new HttpCookie(FormsAuthentication.FormsCookieName, encTicket);
        Response.Cookies.Add(faCookie);
    
        return RedirectToAction("Index", "Home");
    }
  5. Global.asax.cs - Odczytywanie plików cookie i zastępowanie obiektu HttpContext.User odbywa się to przez przesłanianie PostAuthenticateRequest

    protected void Application_PostAuthenticateRequest(Object sender, EventArgs e)
    {
        HttpCookie authCookie = Request.Cookies[FormsAuthentication.FormsCookieName];
    
        if (authCookie != null)
        {
            FormsAuthenticationTicket authTicket = FormsAuthentication.Decrypt(authCookie.Value);
    
            JavaScriptSerializer serializer = new JavaScriptSerializer();
    
            CustomPrincipalSerializeModel serializeModel = serializer.Deserialize<CustomPrincipalSerializeModel>(authTicket.UserData);
    
            CustomPrincipal newUser = new CustomPrincipal(authTicket.Name);
            newUser.Id = serializeModel.Id;
            newUser.FirstName = serializeModel.FirstName;
            newUser.LastName = serializeModel.LastName;
    
            HttpContext.Current.User = newUser;
        }
    }
  6. Dostęp w widokach Razor

    @((User as CustomPrincipal).Id)
    @((User as CustomPrincipal).FirstName)
    @((User as CustomPrincipal).LastName)

i w kodzie:

    (User as CustomPrincipal).Id
    (User as CustomPrincipal).FirstName
    (User as CustomPrincipal).LastName

Myślę, że kod jest oczywisty. Jeśli nie, daj mi znać.

Dodatkowo, aby jeszcze łatwiej uzyskać dostęp, możesz utworzyć kontroler podstawowy i zastąpić zwrócony obiekt użytkownika (HttpContext.User):

public class BaseController : Controller
{
    protected virtual new CustomPrincipal User
    {
        get { return HttpContext.User as CustomPrincipal; }
    }
}

a następnie dla każdego kontrolera:

public class AccountController : BaseController
{
    // ...
}

co pozwoli ci uzyskać dostęp do niestandardowych pól w kodzie:

User.Id
User.FirstName
User.LastName

Ale to nie zadziała w widokach. W tym celu musisz utworzyć niestandardową implementację WebViewPage:

public abstract class BaseViewPage : WebViewPage
{
    public virtual new CustomPrincipal User
    {
        get { return base.User as CustomPrincipal; }
    }
}

public abstract class BaseViewPage<TModel> : WebViewPage<TModel>
{
    public virtual new CustomPrincipal User
    {
        get { return base.User as CustomPrincipal; }
    }
}

Ustaw jako domyślny typ strony w Views / web.config:

<pages pageBaseType="Your.Namespace.BaseViewPage">
  <namespaces>
    <add namespace="System.Web.Mvc" />
    <add namespace="System.Web.Mvc.Ajax" />
    <add namespace="System.Web.Mvc.Html" />
    <add namespace="System.Web.Routing" />
  </namespaces>
</pages>

a w widokach możesz uzyskać do niego dostęp w następujący sposób:

@User.FirstName
@User.LastName
LukeP
źródło
9
Niezła realizacja; uważaj na funkcję RoleManagerModule zastępującą niestandardową jednostkę główną wartością RolePrincipal.
Sprawiło
9
ok znalazłem rozwiązanie, po prostu dodaj przełącznik else, który przekazuje „” (pusty ciąg) jako wiadomość e-mail, a tożsamość będzie anonimowa.
Pierre-Alain Vigeant,
3
DateTime.Now.AddMinutes (N) ... jak to zrobić, aby nie wylogował użytkownika po N minutach, czy zalogowany użytkownik może zostać utrwalony (na przykład, gdy użytkownik zaznaczy „Pamiętaj mnie”)?
1110
4
Jeśli korzystasz z WebApiController, musisz ustawić Thread.CurrentPrincipalat Application_PostAuthenticateRequest, aby działał, ponieważ nie zależy od niegoHttpContext.Current.User
Jonathan Levison
3
@AbhinavGujjar FormsAuthentication.SignOut();działa dla mnie dobrze.
LukeP
109

Nie mogę mówić bezpośrednio w przypadku ASP.NET MVC, ale w przypadku formularzy internetowych ASP.NET sztuczka polega na utworzeniu pliku FormsAuthenticationTicketi zaszyfrowaniu go w pliku cookie po uwierzytelnieniu użytkownika. W ten sposób wystarczy zadzwonić do bazy danych tylko raz (lub AD lub cokolwiek, czego używasz do wykonania uwierzytelnienia), a każde kolejne żądanie zostanie uwierzytelnione na podstawie biletu zapisanego w pliku cookie.

Dobry artykuł na ten temat: http://www.ondotnet.com/pub/a/dotnet/2004/02/02/effectiveformsauth.html (uszkodzony link)

Edytować:

Ponieważ powyższy link jest zepsuty, poleciłbym rozwiązanie LukeP w jego odpowiedzi powyżej: https://stackoverflow.com/a/10524305 - Sugeruję również zmianę przyjętej odpowiedzi na tę.

Edycja 2: Alternatywa dla uszkodzonego linku: https://web.archive.org/web/20120422011422/http://ondotnet.com/pub/a/dotnet/2004/02/02/effectiveformsauth.html

John Rasch
źródło
Pochodzę z PHP, zawsze umieszczam takie informacje, jak UserID i inne elementy potrzebne do udzielenia ograniczonego dostępu w sesji. Przechowywanie go po stronie klienta denerwuje mnie, czy możesz skomentować, dlaczego nie będzie to problemem?
John Zumbrum
@JohnZ - sam bilet jest szyfrowany na serwerze, zanim zostanie wysłany przewodowo, więc nie jest tak, że klient będzie miał dostęp do danych przechowywanych w bilecie. Pamiętaj, że identyfikatory sesji są również przechowywane w pliku cookie, więc tak naprawdę nie jest inaczej.
John Rasch
3
Jeśli jesteś tutaj, powinieneś spojrzeć na rozwiązanie
LukeP
2
Zawsze podejrzewałem możliwość przekroczenia maksymalnego rozmiaru pliku cookie ( stackoverflow.com/questions/8706924/… ) przy takim podejściu. Zwykle używam ich Cachejako Sessionzamiennika do przechowywania danych na serwerze. Czy ktoś może mi powiedzieć, czy jest to wadliwe podejście?
Red Taz
2
Niezłe podejście. Jednym z potencjalnych problemów jest to, że jeśli obiekt użytkownika ma więcej niż kilka właściwości (a zwłaszcza jeśli są to obiekty zagnieżdżone), utworzenie pliku cookie zakończy się niepowodzeniem, gdy zaszyfrowana wartość przekroczy 4KB (znacznie łatwiej jest trafić, niż myślisz). Jeśli przechowujesz tylko kluczowe dane, wszystko jest w porządku, ale do końca musiałbyś wcisnąć DB. Inną kwestią jest „aktualizacja” danych cookie, gdy obiekt użytkownika ma zmiany w sygnaturze lub logice.
Geoffrey Hudik,
63

Oto przykład wykonania pracy. bool isValid jest ustawiany przez przeglądanie jakiegoś magazynu danych (powiedzmy, że twoja baza danych użytkownika). UserID to tylko identyfikator, który utrzymuję. Możesz dodać dodatkowe dane, takie jak adres e-mail, do danych użytkownika.

protected void btnLogin_Click(object sender, EventArgs e)
{         
    //Hard Coded for the moment
    bool isValid=true;
    if (isValid) 
    {
         string userData = String.Empty;
         userData = userData + "UserID=" + userID;
         FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(1, username, DateTime.Now, DateTime.Now.AddMinutes(30), true, userData);
         string encTicket = FormsAuthentication.Encrypt(ticket);
         HttpCookie faCookie = new HttpCookie(FormsAuthentication.FormsCookieName, encTicket);
         Response.Cookies.Add(faCookie);
         //And send the user where they were heading
         string redirectUrl = FormsAuthentication.GetRedirectUrl(username, false);
         Response.Redirect(redirectUrl);
     }
}

w golbal asax dodaj następujący kod, aby odzyskać informacje

protected void Application_AuthenticateRequest(Object sender, EventArgs e)
{
    HttpCookie authCookie = Request.Cookies[
             FormsAuthentication.FormsCookieName];
    if(authCookie != null)
    {
        //Extract the forms authentication cookie
        FormsAuthenticationTicket authTicket = 
               FormsAuthentication.Decrypt(authCookie.Value);
        // Create an Identity object
        //CustomIdentity implements System.Web.Security.IIdentity
        CustomIdentity id = GetUserIdentity(authTicket.Name);
        //CustomPrincipal implements System.Web.Security.IPrincipal
        CustomPrincipal newUser = new CustomPrincipal();
        Context.User = newUser;
    }
}

Aby później skorzystać z tych informacji, możesz uzyskać dostęp do niestandardowej nazwy użytkownika w następujący sposób.

(CustomPrincipal)this.User
or 
(CustomPrincipal)this.Context.User

pozwoli ci to uzyskać dostęp do niestandardowych informacji o użytkowniku.

Sriwantha Attanayake
źródło
2
FYI - to Request.Cookies [] (liczba mnoga)
Dan Esparza
10
Nie zapomnij ustawić Thread.CurrentPrincipal oraz Context.User na CustomPrincipal.
Russ Cam
6
Skąd pochodzi GetUserIdentity ()?
Ryan,
Jak wspomniałem w komentarzu, daje on implementację System.Web.Security.IIdentity. Google o tym interfejsie
Sriwantha Attanayake
16

MVC udostępnia metodę OnAuthorize, która zawiesza się z klas kontrolerów. Lub możesz użyć niestandardowego filtra akcji, aby wykonać autoryzację. MVC sprawia, że ​​jest to dość łatwe. Zamieściłem tutaj post na blogu. http://www.bradygaster.com/post/custom-authentication-with-mvc-3.0

Brady Gaster
źródło
Ale sesja może zostać utracona, a użytkownik nadal się uwierzytelnia. Nie?
Dragouf,
@brady gaster, czytam twój post na blogu (dzięki!), Dlaczego ktoś miałby używać zastąpienia „OnAuthorize ()”, jak wspomniano w twoim poście, nad wpisem global.asax „... AuthenticateRequest (..)” wspomnianym przez innego odpowiedzi? Czy jedno jest preferowane w ustawianiu głównego użytkownika?
RayLoveless 11.04.16
10

Oto rozwiązanie, jeśli musisz podłączyć pewne metody do @User do użycia w twoich widokach. Nie ma rozwiązania dla jakiejkolwiek poważnej personalizacji członkostwa, ale gdyby pierwotne pytanie było potrzebne tylko dla widoków, być może to wystarczyłoby. Poniższe informacje wykorzystano do sprawdzenia zmiennej zwróconej z filtru autoryzacji, służącej do sprawdzenia, czy niektóre łącza nie mają być prezentowane, czy też nie (nie w przypadku logiki autoryzacji lub przyznania dostępu).

using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web;
    using System.Security.Principal;

    namespace SomeSite.Web.Helpers
    {
        public static class UserHelpers
        {
            public static bool IsEditor(this IPrincipal user)
            {
                return null; //Do some stuff
            }
        }
    }

Następnie po prostu dodaj odniesienie w obszarach web.config i nazwij je jak poniżej w widoku.

@User.IsEditor()
Baza
źródło
1
W twoim rozwiązaniu znów musimy wykonywać połączenia z bazą danych za każdym razem. Ponieważ obiekt użytkownika nie ma właściwości niestandardowych. Ma tylko nazwę i IsAuthanticated
oneNiceFriend
Zależy to całkowicie od wdrożenia i pożądanego zachowania. Moja próbka zawiera 0 linii logiki bazy danych lub roli. Jeśli ktoś użyje IsInRole, to z kolei może zostać zapisany w pamięci podręcznej w pliku cookie. Lub wdrażasz własną logikę buforowania.
Baza
3

Na podstawie odpowiedzi LukeP i dodaj kilka metod konfiguracji timeouti requireSSLwspółpracyWeb.config .

Odnośniki do linków

Zmodyfikowane kody LukeP

1, Ustaw timeoutna podstawie Web.Config. FormsAuthentication.Timeout dostanie wartość limitu czasu, który jest zdefiniowany w pliku web.config. Zapakowałem następujące funkcje, które są funkcją zwrotną ticket.

int version = 1;
DateTime now = DateTime.Now;

// respect to the `timeout` in Web.config.
TimeSpan timeout = FormsAuthentication.Timeout;
DateTime expire = now.Add(timeout);
bool isPersist = false;

FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(
     version,          
     name,
     now,
     expire,
     isPersist,
     userData);

2, Skonfiguruj plik cookie, aby był bezpieczny lub nie, w zależności od RequireSSLkonfiguracji.

HttpCookie faCookie = new HttpCookie(FormsAuthentication.FormsCookieName, encTicket);
// respect to `RequreSSL` in `Web.Config`
bool bSSL = FormsAuthentication.RequireSSL;
faCookie.Secure = bSSL;
AechoLiu
źródło
3

W porządku, więc jestem poważnym kryptowalutą, przeciągając to bardzo stare pytanie, ale istnieje o wiele prostsze podejście do tego, o czym wspomniał @Baserz powyżej. I to jest użycie kombinacji metod rozszerzenia C # i buforowania (NIE używaj sesji).

W rzeczywistości Microsoft dostarczył już wiele takich rozszerzeń w Microsoft.AspNet.Identity.IdentityExtensionsprzestrzeni nazw. Na przykład GetUserId()jest metodą rozszerzenia, która zwraca identyfikator użytkownika. Jest też GetUserName()iFindFirstValue() , który zwraca roszczenia na podstawie IPrincipal.

Musisz tylko podać przestrzeń nazw, a następnie zadzwonić User.Identity.GetUserName() aby uzyskać nazwę użytkownika skonfigurowaną przez ASP.NET Identity.

Nie jestem pewien, czy jest to buforowane, ponieważ starsza tożsamość ASP.NET nie jest open source, i nie zadałem sobie trudu, aby ją odtworzyć. Jeśli jednak tak nie jest, możesz napisać własną metodę rozszerzenia, która będzie buforować ten wynik przez określony czas.

Erik Funkenbusch
źródło
Dlaczego „nie używaj sesji”?
Alex
@jitbit - ponieważ sesja jest niewiarygodna i niepewna. Z tego samego powodu nigdy nie należy używać sesji ze względów bezpieczeństwa.
Erik Funkenbusch,
„Niewiarygodne” można rozwiązać przez ponowne wypełnienie sesji (jeśli jest puste). „Niezabezpieczony” - istnieją sposoby ochrony przed przejęciem sesji (używając tylko HTTPS + inne sposoby). Ale tak naprawdę się z tobą zgadzam. Gdzie byś to buforował? Informacje jak IsUserAdministratorlub UserEmailitp? Myślisz HttpRuntime.Cache?
Alex
@jitbit - To jedna z opcji lub inne rozwiązanie buforowania, jeśli masz. Upewnij się, że wygaśnie wpis pamięci podręcznej po pewnym czasie. Niepewne dotyczy również systemu lokalnego, ponieważ możesz ręcznie zmienić plik cookie i odgadnąć identyfikatory sesji. Mężczyzna pośrodku to nie jedyne zmartwienie.
Erik Funkenbusch,
2

Jako dodatek do kodu LukeP dla użytkowników formularzy internetowych (nie MVC), jeśli chcesz uprościć dostęp do kodu za twoimi stronami, po prostu dodaj poniższy kod do strony podstawowej i uzyskaj stronę podstawową na wszystkich swoich stronach:

Public Overridable Shadows ReadOnly Property User() As CustomPrincipal
    Get
        Return DirectCast(MyBase.User, CustomPrincipal)
    End Get
End Property

Tak więc w swoim kodzie możesz po prostu uzyskać dostęp:

User.FirstName or User.LastName

W scenariuszu z formularzem sieciowym brakuje mi tego, jak uzyskać takie samo zachowanie w kodzie niepowiązanym ze stroną, na przykład w httpmodules czy zawsze powinienem dodawać rzutowanie w każdej klasie, czy też jest lepszy sposób na uzyskanie tego?

Dzięki za odpowiedzi i dziękujemy LukeP ponieważ użyłem swoich przykłady jako baza dla mojego niestandardowego użytkownika (który ma teraz User.Roles, User.Tasks, User.HasPath(int), User.Settings.Timeouti wiele innych rzeczy ładne)

Manight
źródło
0

Wypróbowałem rozwiązanie sugerowane przez LukeP i okazało się, że nie obsługuje ono atrybutu Autoryzuj. Więc trochę to zmodyfikowałem.

public class UserExBusinessInfo
{
    public int BusinessID { get; set; }
    public string Name { get; set; }
}

public class UserExInfo
{
    public IEnumerable<UserExBusinessInfo> BusinessInfo { get; set; }
    public int? CurrentBusinessID { get; set; }
}

public class PrincipalEx : ClaimsPrincipal
{
    private readonly UserExInfo userExInfo;
    public UserExInfo UserExInfo => userExInfo;

    public PrincipalEx(IPrincipal baseModel, UserExInfo userExInfo)
        : base(baseModel)
    {
        this.userExInfo = userExInfo;
    }
}

public class PrincipalExSerializeModel
{
    public UserExInfo UserExInfo { get; set; }
}

public static class IPrincipalHelpers
{
    public static UserExInfo ExInfo(this IPrincipal @this) => (@this as PrincipalEx)?.UserExInfo;
}


    [HttpPost]
    [AllowAnonymous]
    [ValidateAntiForgeryToken]
    public async Task<ActionResult> Login(LoginModel details, string returnUrl)
    {
        if (ModelState.IsValid)
        {
            AppUser user = await UserManager.FindAsync(details.Name, details.Password);

            if (user == null)
            {
                ModelState.AddModelError("", "Invalid name or password.");
            }
            else
            {
                ClaimsIdentity ident = await UserManager.CreateIdentityAsync(user, DefaultAuthenticationTypes.ApplicationCookie);
                AuthManager.SignOut();
                AuthManager.SignIn(new AuthenticationProperties { IsPersistent = false }, ident);

                user.LastLoginDate = DateTime.UtcNow;
                await UserManager.UpdateAsync(user);

                PrincipalExSerializeModel serializeModel = new PrincipalExSerializeModel();
                serializeModel.UserExInfo = new UserExInfo()
                {
                    BusinessInfo = await
                        db.Businesses
                        .Where(b => user.Id.Equals(b.AspNetUserID))
                        .Select(b => new UserExBusinessInfo { BusinessID = b.BusinessID, Name = b.Name })
                        .ToListAsync()
                };

                JavaScriptSerializer serializer = new JavaScriptSerializer();

                string userData = serializer.Serialize(serializeModel);

                FormsAuthenticationTicket authTicket = new FormsAuthenticationTicket(
                         1,
                         details.Name,
                         DateTime.Now,
                         DateTime.Now.AddMinutes(15),
                         false,
                         userData);

                string encTicket = FormsAuthentication.Encrypt(authTicket);
                HttpCookie faCookie = new HttpCookie(FormsAuthentication.FormsCookieName, encTicket);
                Response.Cookies.Add(faCookie);

                return RedirectToLocal(returnUrl);
            }
        }
        return View(details);
    }

I wreszcie w Global.asax.cs

    protected void Application_PostAuthenticateRequest(Object sender, EventArgs e)
    {
        HttpCookie authCookie = Request.Cookies[FormsAuthentication.FormsCookieName];

        if (authCookie != null)
        {
            FormsAuthenticationTicket authTicket = FormsAuthentication.Decrypt(authCookie.Value);
            JavaScriptSerializer serializer = new JavaScriptSerializer();
            PrincipalExSerializeModel serializeModel = serializer.Deserialize<PrincipalExSerializeModel>(authTicket.UserData);
            PrincipalEx newUser = new PrincipalEx(HttpContext.Current.User, serializeModel.UserExInfo);
            HttpContext.Current.User = newUser;
        }
    }

Teraz mogę uzyskać dostęp do danych w widokach i kontrolerach, po prostu dzwoniąc

User.ExInfo()

Aby się wylogować, po prostu dzwonię

AuthManager.SignOut();

gdzie jest AuthManager

HttpContext.GetOwinContext().Authentication
Wasilij Iwanow
źródło