ASP.NET MVC - Tetapkan IIdentity kustom atau IPrincipal

650

Saya perlu melakukan sesuatu yang cukup sederhana: dalam aplikasi ASP.NET MVC saya, saya ingin menetapkan kebiasaan IIdentity / IPrincipal. Mana yang lebih mudah / lebih cocok. Saya ingin memperpanjang default sehingga saya bisa memanggil sesuatu seperti User.Identity.Iddan User.Identity.Role. Tidak ada yang mewah, hanya beberapa properti tambahan.

Saya telah membaca banyak artikel dan pertanyaan, tetapi saya merasa saya membuatnya lebih sulit dari yang sebenarnya. Saya pikir itu akan mudah. Jika pengguna log on, saya ingin mengatur IIdentity kustom. Jadi saya pikir, saya akan menerapkannya Application_PostAuthenticateRequestdi global.asax saya. Namun, itu dipanggil pada setiap permintaan, dan saya tidak ingin melakukan panggilan ke database pada setiap permintaan yang akan meminta semua data dari database dan memasukkan objek IPrincipal kustom. Itu juga tampaknya sangat tidak perlu, lambat, dan di tempat yang salah (melakukan panggilan basis data di sana) tetapi saya bisa saja salah. Atau dari mana lagi data itu berasal?

Jadi saya pikir, setiap kali pengguna login, saya bisa menambahkan beberapa variabel yang diperlukan dalam sesi saya, yang saya tambahkan ke kustom IIdentity di Application_PostAuthenticateRequestpengendali event. Namun, saya Context.Sessionadalah nullada, sehingga juga tidak cara untuk pergi.

Saya sudah mengerjakan ini selama sehari sekarang dan saya merasa saya kehilangan sesuatu. Ini seharusnya tidak terlalu sulit untuk dilakukan, bukan? Saya juga agak bingung dengan semua hal (semi) terkait yang datang dengan ini. MembershipProvider, MembershipUser, RoleProvider, ProfileProvider, IPrincipal, IIdentity, FormsAuthentication.... Am saya satu-satunya yang menemukan semua ini sangat membingungkan?

Jika seseorang bisa memberi tahu saya solusi sederhana, elegan, dan efisien untuk menyimpan beberapa data tambahan pada IIdentity tanpa semua fuzz ekstra .. itu akan luar biasa! Saya tahu ada pertanyaan serupa tentang SO, tetapi jika jawaban yang saya butuhkan ada di sana, saya pasti mengabaikannya.

Razzie
sumber
1
Hai Domi, ini kombinasi hanya menyimpan data yang tidak pernah berubah (seperti ID pengguna) atau memperbarui cookie langsung setelah pengguna mengubah data yang harus tercermin dalam cookie segera. Jika pengguna melakukan itu, saya cukup memperbarui cookie dengan data baru. Tetapi saya mencoba untuk tidak menyimpan data yang sering berubah.
Razzie
26
pertanyaan ini memiliki 36k tampilan dan banyak upvotes. apakah ini benar-benar persyaratan umum - dan jika demikian, tidakkah ada cara yang lebih baik daripada semua 'barang khusus' ini?
Simon_Weaver
2
@Simon_Weaver Ada ASP.NET Identity know, yang mendukung informasi khusus tambahan dalam cookie terenkripsi dengan lebih mudah.
John
1
Saya setuju dengan Anda, ada banyak informasi seperti Anda diposting: MemberShip..., Principal, Identity. ASP.NET harus membuat ini lebih mudah, lebih sederhana dan paling banyak dua pendekatan untuk menangani otentikasi.
broadband
1
@Simon_Weaver Ini jelas menunjukkan ada permintaan untuk sistem identitas IMHO lebih mudah lebih mudah lebih fleksibel.
niico

Jawaban:

838

Begini cara saya melakukannya.

Saya memutuskan untuk menggunakan IPrincipal daripada IIdentity karena itu berarti saya tidak harus mengimplementasikan IIdentity dan IPrincipal.

  1. Buat antarmuka

    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 - untuk membuat serialisasi informasi khusus ke dalam bidang data pengguna di objek FormsAuthenticationTicket.

    public class CustomPrincipalSerializeModel
    {
        public int Id { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
    }
  4. Metode LogIn - mengatur cookie dengan informasi khusus

    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 - Membaca cookie dan mengganti objek HttpContext.User, ini dilakukan dengan mengganti 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. Akses dalam tampilan Razor

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

dan dalam kode:

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

Saya pikir kodenya cukup jelas. Jika tidak, beri tahu saya.

Selain itu untuk membuat akses lebih mudah, Anda dapat membuat pengontrol dasar dan menimpa objek Pengguna yang dikembalikan (HttpContext.User):

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

dan kemudian, untuk setiap pengontrol:

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

yang akan memungkinkan Anda untuk mengakses bidang khusus dalam kode seperti ini:

User.Id
User.FirstName
User.LastName

Tapi ini tidak akan berfungsi dalam tampilan. Untuk itu, Anda perlu membuat implementasi WebViewPage khusus:

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

Jadikan ini tipe halaman default di 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>

dan dalam tampilan, Anda dapat mengaksesnya seperti ini:

@User.FirstName
@User.LastName
LukeP
sumber
9
Implementasi yang bagus; perhatikan RoleManagerModule mengganti prinsipal khusus Anda dengan RolePrincipal. Itu menyebabkan saya sangat sakit - stackoverflow.com/questions/10742259/...
David Keaveny
9
ok saya menemukan solusinya, tambahkan saja saklar lain yang lewat "" (string kosong) karena email dan Identitas akan anonim.
Pierre-Alain Vigeant
3
DateTime.Now.AddMinutes (N) ... bagaimana membuat ini agar tidak keluar setelah N menit, bisakah pengguna yang login tetap ada (ketika pengguna memeriksa 'Remember Me' misalnya)?
1110
4
Jika Anda menggunakan WebApiController, Anda akan perlu set Thread.CurrentPrincipaldi Application_PostAuthenticateRequestuntuk itu untuk bekerja karena tidak bergantung padaHttpContext.Current.User
Jonathan Levison
3
@AbhinavGujjar FormsAuthentication.SignOut();berfungsi dengan baik untuk saya.
LukeP
109

Saya tidak dapat berbicara secara langsung untuk ASP.NET MVC, tetapi untuk Formulir Web ASP.NET, triknya adalah membuat FormsAuthenticationTicketdan mengenkripsinya menjadi cookie setelah pengguna diautentikasi. Dengan cara ini, Anda hanya perlu memanggil database sekali (atau AD atau apa pun yang Anda gunakan untuk melakukan otentikasi Anda), dan setiap permintaan berikutnya akan mengotentikasi berdasarkan tiket yang disimpan dalam cookie.

Artikel bagus tentang ini: http://www.ondotnet.com/pub/a/dotnet/2004/02/02/effectiveformsauth.html (tautan rusak)

Edit:

Karena tautan di atas rusak, saya akan merekomendasikan solusi LukeP dalam jawabannya di atas: https://stackoverflow.com/a/10524305 - Saya juga menyarankan agar jawaban yang diterima diubah ke yang itu.

Sunting 2: Alternatif untuk tautan yang rusak: https://web.archive.org/web/20120422011422/http://ondotnet.com/pub/a/dotnet/2004/02/02/effectiveformsauth.html

John Rasch
sumber
Berasal dari PHP, saya selalu memasukkan informasi seperti UserID dan bagian lain yang diperlukan untuk memberikan akses terbatas di Sesi. Menyimpannya di sisi klien membuat saya gugup, dapatkah Anda mengomentari mengapa itu tidak menjadi masalah?
John Zumbrum
@ JohnZ - tiket itu sendiri dienkripsi di server sebelum dikirim melalui kabel, jadi tidak seperti klien akan memiliki akses ke data yang disimpan dalam tiket. Perhatikan bahwa ID sesi juga disimpan dalam cookie, jadi tidak terlalu berbeda.
John Rasch
3
Jika Anda di sini, Anda harus melihat solusi
LukeP
2
Saya selalu khawatir dengan potensi untuk melampaui ukuran cookie maksimum ( stackoverflow.com/questions/8706924/… ) dengan pendekatan ini. Saya cenderung menggunakan Cachesebagai Sessionpengganti untuk menyimpan data di server. Adakah yang bisa memberi tahu saya jika ini adalah pendekatan yang cacat?
Red Taz
2
Pendekatan yang bagus. Salah satu masalah potensial dengan ini adalah jika objek pengguna Anda memiliki lebih dari beberapa properti (dan terutama jika ada objek bersarang), membuat cookie akan gagal secara diam-diam setelah nilai terenkripsi lebih dari 4KB (jauh lebih mudah untuk memukul maka Anda mungkin berpikir). Jika Anda hanya menyimpan data kunci tidak apa-apa tetapi Anda harus menekan DB untuk yang lainnya. Pertimbangan lain adalah "memutakhirkan" data cookie ketika objek pengguna memiliki perubahan tanda tangan atau logika.
Geoffrey Hudik
63

Ini adalah contoh untuk menyelesaikan pekerjaan. bool isValid diatur dengan melihat beberapa penyimpanan data (katakanlah basis data pengguna Anda). UserID hanya ID yang saya pertahankan. Anda dapat menambahkan informasi tambahan seperti alamat email ke data pengguna.

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

di golbal asax tambahkan kode berikut untuk mengambil kembali informasi Anda

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

Saat Anda akan menggunakan informasi nanti, Anda dapat mengakses kepala sekolah kustom Anda sebagai berikut.

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

ini akan memungkinkan Anda untuk mengakses informasi pengguna khusus.

Sriwantha Attanayake
sumber
2
FYI - ini Request.Cookies [] (jamak)
Dan Esparza
10
Jangan lupa untuk mengatur Thread.CurrentPrincipal serta Context.User ke CustomPrincipal.
Russ Cam
6
Dari mana GetUserIdentity () berasal?
Ryan
Seperti yang telah saya sebutkan di komentar, ini memberikan implementasi System.Web.Security.IIdentity. Google tentang antarmuka itu
Sriwantha Attanayake
16

MVC memberi Anda metode OnAuthorize yang tergantung dari kelas controller Anda. Atau, Anda dapat menggunakan filter tindakan khusus untuk melakukan otorisasi. MVC membuatnya sangat mudah dilakukan. Saya memposting posting blog tentang ini di sini. http://www.bradygaster.com/post/custom-authentication-with-mvc-3.0

brady gaster
sumber
Tetapi sesi bisa hilang dan pengguna masih mengautentikasi. Tidak ?
Dragouf
@ brady gaster, saya membaca posting blog Anda (terima kasih!), Mengapa seseorang menggunakan override "OnAuthorize ()" seperti yang disebutkan pada posting Anda pada entri global.asax "... AuthenticateRequest (..)" disebutkan oleh yang lain jawaban? Apakah satu lebih disukai daripada yang lain dalam menetapkan pengguna prinsip?
Ray Tanpa Cinta
10

Berikut adalah solusi jika Anda perlu menghubungkan beberapa metode ke @User untuk digunakan dalam pandangan Anda. Tidak ada solusi untuk kustomisasi keanggotaan serius, tetapi jika pertanyaan awal diperlukan untuk pandangan saja maka ini mungkin sudah cukup. Di bawah ini digunakan untuk memeriksa variabel yang dikembalikan dari filter otorisasi, digunakan untuk memverifikasi apakah beberapa tautan akan ditampilkan atau tidak (tidak untuk segala jenis logika otorisasi atau pemberian akses).

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

Kemudian tambahkan saja referensi di area web.config, dan beri nama seperti di bawah ini dalam tampilan.

@User.IsEditor()
Mendasarkan
sumber
1
Dalam solusi Anda, Kami sekali lagi perlu melakukan panggilan basis data setiap kali. Karena objek pengguna tidak memiliki properti khusus. Itu hanya memiliki Name dan IsAuthanticated
oneNiceFriend
Itu sepenuhnya tergantung pada implementasi Anda dan perilaku yang diinginkan. Sampel saya berisi 0 baris database, atau peran, logika. Jika seseorang menggunakan IsInRole itu bisa di-cache dalam cookie saya percaya. Atau Anda menerapkan logika cache Anda sendiri.
Pangkalan
3

Berdasarkan jawaban LukeP , dan menambahkan beberapa metode untuk mengatur timeoutdan requireSSLbekerja sama dengan Web.config.

Tautan referensi

Kode-kode LukeP yang Dimodifikasi

1, Set timeoutberdasarkan Web.Config. The FormsAuthentication.Timeout akan mendapatkan nilai timeout, yang didefinisikan dalam web.config. Saya membungkus berikut ini menjadi fungsi, yang mengembalikan ticketpunggung.

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, Konfigurasikan cookie agar aman atau tidak, berdasarkan pada RequireSSLkonfigurasi.

HttpCookie faCookie = new HttpCookie(FormsAuthentication.FormsCookieName, encTicket);
// respect to `RequreSSL` in `Web.Config`
bool bSSL = FormsAuthentication.RequireSSL;
faCookie.Secure = bSSL;
AechoLiu
sumber
3

Baiklah, jadi saya seorang cryptkeeper serius di sini dengan menyeret pertanyaan yang sangat lama ini, tetapi ada pendekatan yang lebih sederhana untuk ini, yang disentuh oleh @Baserz di atas. Dan itu adalah dengan menggunakan kombinasi metode C # Extension dan caching (JANGAN gunakan sesi).

Bahkan, Microsoft telah menyediakan sejumlah ekstensi seperti itu di Microsoft.AspNet.Identity.IdentityExtensionsnamespace. Misalnya, GetUserId()adalah metode ekstensi yang mengembalikan ID pengguna. Ada juga GetUserName()dan FindFirstValue(), yang mengembalikan klaim berdasarkan IPrincipal.

Jadi, Anda hanya perlu menyertakan namespace, dan kemudian menelepon User.Identity.GetUserName()untuk mendapatkan nama pengguna yang dikonfigurasi oleh ASP.NET Identity.

Saya tidak yakin apakah ini di-cache, karena ASP.NET Identity yang lebih lama tidak bersumber terbuka, dan saya belum repot-repot merekayasa baliknya. Namun, jika tidak maka Anda dapat menulis metode ekstensi Anda sendiri, yang akan menyimpan hasil ini untuk jangka waktu tertentu.

Erik Funkenbusch
sumber
Mengapa "jangan gunakan sesi"?
Alex
@bitbit - karena sesi tidak dapat diandalkan, dan tidak aman. Untuk alasan yang sama Anda tidak boleh menggunakan sesi untuk tujuan keamanan.
Erik Funkenbusch
"Tidak bisa diandalkan" dapat diatasi dengan sesi pengisian ulang (jika kosong). "Tidak aman" - ada cara untuk melindungi dari pembajakan sesi (dengan menggunakan HTTPS-only + cara lain). Tapi saya sebenarnya setuju dengan Anda. Di mana Anda akan menyimpannya? Info suka IsUserAdministratoratau UserEmaildll? Kamu berpikir HttpRuntime.Cache?
Alex
@ jitbit - Itu satu opsi, atau solusi cache lain jika Anda memilikinya. Pastikan untuk kedaluwarsa entri cache setelah jangka waktu tertentu. Tidak aman juga berlaku untuk sistem lokal, karena Anda dapat secara manual mengubah cookie dan menebak ID sesi. Manusia di tengah bukanlah satu-satunya perhatian.
Erik Funkenbusch
2

Sebagai tambahan kode LukeP untuk pengguna Formulir Web (bukan MVC) jika Anda ingin menyederhanakan akses dalam kode di belakang halaman Anda, cukup tambahkan kode di bawah ini ke halaman dasar dan turunkan halaman dasar di semua halaman Anda:

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

Jadi dalam kode Anda di belakang Anda cukup mengakses:

User.FirstName or User.LastName

Apa yang saya lewatkan dalam skenario Formulir Web, adalah bagaimana mendapatkan perilaku yang sama dalam kode yang tidak terikat ke halaman, misalnya dalam httpmodules haruskah saya selalu menambahkan gips di setiap kelas atau apakah ada cara yang lebih cerdas untuk mendapatkan ini?

Terima kasih atas jawaban Anda dan terima kasih kepada LukeP sejak saya menggunakan contoh Anda sebagai dasar untuk pengguna kustom saya (yang sekarang memiliki User.Roles, User.Tasks, User.HasPath(int), User.Settings.Timeoutdan banyak hal-hal baik lainnya)

Manight
sumber
0

Saya mencoba solusi yang disarankan oleh LukeP dan menemukan bahwa itu tidak mendukung atribut Otorisasi. Jadi, saya memodifikasinya sedikit.

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

Dan akhirnya di 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;
        }
    }

Sekarang saya dapat mengakses data dalam tampilan dan pengontrol hanya dengan menelepon

User.ExInfo()

Untuk keluar saya hanya menelepon

AuthManager.SignOut();

di mana AuthManager berada

HttpContext.GetOwinContext().Authentication
Ivanov dengan mudah
sumber