Cara memperluas properti User.Identity yang tersedia

130

Saya menggunakan MVC5 Identity 2.0 agar pengguna dapat masuk ke situs web saya, tempat rincian otentikasi disimpan dalam database SQL. Asp.net Identity telah diimplementasikan dengan cara standar seperti yang dapat ditemukan di banyak tutorial online.

Kelas ApplicationUser di IdentityModels telah diperluas untuk menyertakan beberapa properti kustom, seperti integer OrganizationId. Idenya adalah bahwa banyak pengguna dapat dibuat dan ditugaskan ke Organisasi umum untuk tujuan hubungan basis data.

public class ApplicationUser : IdentityUser
    {
        public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<ApplicationUser> manager)
        {
            // Note the authenticationType must match the one defined in CookieAuthenticationOptions.AuthenticationType
            var userIdentity = await manager.CreateIdentityAsync(this, DefaultAuthenticationTypes.ApplicationCookie);
            // Add custom user claims here
            return userIdentity;
        }

        //Extended Properties
        public DateTime? BirthDate { get; set; }
        public long? OrganizationId { get; set; }

        //Key Mappings
        [ForeignKey("OrganizationId")]
        public virtual Organization Organization { get; set; }
    }

Bagaimana saya bisa mengambil properti OrganizationId dari pengguna yang saat ini masuk dari dalam pengontrol? Apakah ini tersedia melalui metode setelah pengguna login atau apakah saya selalu memiliki mengambil OrganizationId dari database, berdasarkan UserId, setiap kali metode controller dijalankan?

Membaca di web saya telah melihat saya perlu menggunakan yang berikut untuk mendapatkan UserId login dll

using Microsoft.AspNet.Identity;
...
User.Identity.GetUserId();

Namun, OrganizationId bukan properti yang tersedia di User.Identity. Apakah saya perlu memperluas User.Identity untuk memasukkan properti OrganizationId? Jika demikian, bagaimana caranya?

Alasan saya sangat membutuhkan OrganizationId adalah karena banyak permintaan tabel bergantung pada OrganizationId untuk mengambil data yang relevan dengan Organisasi yang terkait dengan pengguna yang masuk.

RobHurd
sumber
3
Apakah jawaban saya di sini membantu Anda?
Sepatu
1
Jawaban yang hampir sama dari saya di sini: stackoverflow.com/a/28138594/809357 - jika Anda membutuhkan informasi ini secara teratur dalam kehidupan permintaan, Anda dapat meletakkannya di cookie sebagai klaim.
trailmax
1
Terima kasih @Sepatu jawaban Anda bekerja. Selain jawaban Anda, saya harus menambahkan klaim untuk disimpan dalam cookie. Di kelas IdentityModels saya harus menambahkan userIdentity.AddClaim (Klaim baru ("MyApp: OrganizationId", OrganizationId.ToString ())); ke tugas async publik <ClaimsIdentity> GenerateUserIdentityAsync (UserManager manajer <ApplicationUser>) metode.
RobHurd

Jawaban:

219

Kapan pun Anda ingin memperluas properti User.Identity dengan properti tambahan seperti pertanyaan di atas, tambahkan properti ini ke kelas ApplicationUser seperti:

public class ApplicationUser : IdentityUser
{
    public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<ApplicationUser> manager)
    {
        // Note the authenticationType must match the one defined in CookieAuthenticationOptions.AuthenticationType
        var userIdentity = await manager.CreateIdentityAsync(this, DefaultAuthenticationTypes.ApplicationCookie);
        // Add custom user claims here
        return userIdentity;
    }

    // Your Extended Properties
    public long? OrganizationId { get; set; }
}

Maka yang Anda butuhkan adalah membuat metode ekstensi seperti itu (saya membuat milik saya di folder Ekstensi baru):

namespace App.Extensions
{
    public static class IdentityExtensions
    {
        public static string GetOrganizationId(this IIdentity identity)
        {
            var claim = ((ClaimsIdentity)identity).FindFirst("OrganizationId");
            // Test for null to avoid issues during local testing
            return (claim != null) ? claim.Value : string.Empty;
        }
    }
}

Ketika Anda membuat Identity di kelas ApplicationUser, cukup tambahkan Klaim -> OrganizationId seperti:

    public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<ApplicationUser> manager)
    {
        // Note the authenticationType must match the one defined in CookieAuthenticationOptions.AuthenticationType
        var userIdentity = await manager.CreateIdentityAsync(this, DefaultAuthenticationTypes.ApplicationCookie);
        // Add custom user claims here => this.OrganizationId is a value stored in database against the user
        userIdentity.AddClaim(new Claim("OrganizationId", this.OrganizationId.ToString()));

        return userIdentity;
    }

Setelah Anda menambahkan klaim dan menerapkan metode ekstensi, untuk menjadikannya tersedia sebagai properti di Pengguna Anda. Identitas, tambahkan pernyataan penggunaan pada halaman / file yang ingin Anda akses :

dalam kasus saya: using App.Extensions;dalam Controller dan @using. App.Extensionswithing .cshtml View file.

EDIT:

Apa yang juga bisa Anda lakukan untuk menghindari menambahkan pernyataan menggunakan di setiap tampilan adalah pergi ke folder Views, dan cari file Web.config di sana. Sekarang cari <namespaces>tag dan tambahkan namespace ekstensi Anda di sana seperti:

<add namespace="App.Extensions" />

Simpan file Anda dan Anda selesai. Sekarang setiap Tampilan akan mengetahui ekstensi Anda.

Anda dapat mengakses Metode Ekstensi:

var orgId = User.Identity.GetOrganizationId();
Pawel
sumber
Hai pawel, saya sudah mencoba yang sama, tetapi mendapatkan kesalahan bahwa pengguna Aplikasi tidak mengandung definisi OrganisationId
Ini jebakan
@RachitGupta Kapan itu terjadi? Ketika Anda mencoba menambahkan klaim atau ketika Anda mencoba mengakses nilainya nanti dalam kode? Jika ketika Anda menambahkan klaim maka pastikan ApplicationUser Anda memiliki properti yang ditentukan ... Jika nanti dalam kode maka jangan lupa untuk menambahkan pernyataan menggunakan ke tempat Anda membuat metode ekstensi seperti: menggunakan App.Extensions ;
Pawel
Mendapat kesalahan pada baris userIdentity.AddClaim. Saya telah membuat kelas IdentityExtensions hanya dalam file IdentityModels.cs. Bisakah itu menjadi sumber masalah?
Ini jebakan
Tidak, jika itu ketika Anda menambahkan klaim itu terjadi jauh sebelum identityExtensions ikut bermain (saat itulah Anda membaca nilai kembali) ... Pastikan ApplicationUser Anda memiliki properti yang Anda coba tambahkan sebagai klaim: dalam contoh ini adalah public long OrganizationId {get; set; }
Pawel
6
Terima kasih, ini berhasil. Saya pikir ini adalah praktik terbaik untuk variabel khusus di Asp Net Idendity 2. Saya tidak tahu mengapa komunitas Asp.Net tidak memberikan contoh seperti itu dalam artikel default di situs web mereka.
oneNiceFriend
17

Saya mencari solusi yang sama dan Pawel memberi saya 99% jawabannya. Satu-satunya hal yang hilang yang saya perlukan untuk menampilkan ekstensi adalah menambahkan Kode Razor berikut ke halaman cshtml (view):

@using programname.Models.Extensions

Saya mencari FirstName, untuk ditampilkan di kanan atas NavBar saya setelah pengguna masuk.

Saya pikir saya akan memposting hal ini membantu orang lain, Jadi di sini adalah kode saya:

Saya membuat folder baru bernama Extensions (Under my Models Folder) dan membuat kelas baru seperti Pawel yang ditentukan di atas: IdentityExtensions.cs

using System.Security.Claims;
using System.Security.Principal;

namespace ProgramName.Models.Extensions
{
    public static class IdentityExtensions
    {
        public static string GetUserFirstname(this IIdentity identity)
        {
            var claim = ((ClaimsIdentity)identity).FindFirst("FirstName");
            // Test for null to avoid issues during local testing
            return (claim != null) ? claim.Value : string.Empty;
        }
    }
}

IdentityModels.cs :

public class ApplicationUser : IdentityUser
{

    //Extended Properties
    public string FirstName { get; internal set; }
    public string Surname { get; internal set; }
    public bool isAuthorized { get; set; }
    public bool isActive { get; set; }

    public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<ApplicationUser> manager)
    {
        // Note the authenticationType must match the one defined in CookieAuthenticationOptions.AuthenticationType
        var userIdentity = await manager.CreateIdentityAsync(this, DefaultAuthenticationTypes.ApplicationCookie);
        // Add custom user claims here
        userIdentity.AddClaim(new Claim("FirstName", this.FirstName));

        return userIdentity;
    }
}

Lalu di _LoginPartial.cshtml( Views/SharedFolder) saya, saya menambahkan@using.ProgramName.Models.Extensions

Saya kemudian menambahkan perubahan ke baris kode berikut yang akan menggunakan nama depan pengguna setelah masuk:

@Html.ActionLink("Hello " + User.Identity.GetUserFirstname() + "!", "Index", "Manage", routeValues: null, htmlAttributes: new { title = "Manage" })

Mungkin ini membantu orang lain di telepon.

AxleWack
sumber
11

Lihat posting blog yang hebat ini oleh John Atten: ASP.NET Identity 2.0: Menyesuaikan Pengguna dan Peran

Ini memiliki info langkah-demi-langkah yang hebat tentang keseluruhan proses. Bacalah:)

Berikut adalah beberapa dasar-dasarnya.

Perpanjang kelas ApplicationUser default dengan menambahkan properti baru (mis. Alamat, Kota, Negara Bagian, dll.):

public class ApplicationUser : IdentityUser
{
    public async Task<ClaimsIdentity> 
    GenerateUserIdentityAsync(UserManager<ApplicationUser> manager)
    {
        var userIdentity = await manager.CreateIdentityAsync(this,  DefaultAuthenticationTypes.ApplicationCookie);
        return userIdentity;
    }
    public string Address { get; set; }
    public string City { get; set; }
    public string State { get; set; }

    // Use a sensible display name for views:
    [Display(Name = "Postal Code")]
    public string PostalCode { get; set; }

    // Concatenate the address info for display in tables and such:
    public string DisplayAddress
    {
        get
        {
            string dspAddress = string.IsNullOrWhiteSpace(this.Address) ? "" : this.Address;
            string dspCity = string.IsNullOrWhiteSpace(this.City) ? "" : this.City;
            string dspState = string.IsNullOrWhiteSpace(this.State) ? "" : this.State;
            string dspPostalCode = string.IsNullOrWhiteSpace(this.PostalCode) ? "" : this.PostalCode;

            return string.Format("{0} {1} {2} {3}", dspAddress, dspCity, dspState, dspPostalCode);
        }
    }

Kemudian Anda menambahkan properti baru Anda ke RegisterViewModel Anda.

    // Add the new address properties:
    public string Address { get; set; }
    public string City { get; set; }
    public string State { get; set; }

Kemudian perbarui Tampilan Daftar untuk memasukkan properti baru.

    <div class="form-group">
        @Html.LabelFor(m => m.Address, new { @class = "col-md-2 control-label" })
        <div class="col-md-10">
            @Html.TextBoxFor(m => m.Address, new { @class = "form-control" })
        </div>
    </div>

Kemudian perbarui metode Register () pada AccountController dengan properti baru.

    // Add the Address properties:
    user.Address = model.Address;
    user.City = model.City;
    user.State = model.State;
    user.PostalCode = model.PostalCode;
Knalpot
sumber
16
Ini adalah contoh yang baik tetapi tidak menjawab pertanyaan, bagaimana Anda mendapatkan properti baru dari User.Identity.
Dejan Bogatinovski
5
Diturunkan karena jawabannya tidak menunjukkan bagaimana properti kustom dapat diambil dari User.Identity.
maulik13
3

Bagi siapa pun yang menemukan pertanyaan ini mencari cara mengakses properti kustom di ASP.NET Core 2.1 - itu jauh lebih mudah: Anda akan memiliki UserManager, misalnya di _LoginPartial.cshtml, dan kemudian Anda bisa melakukannya (dengan asumsi "ScreenName" adalah properti yang telah Anda tambahkan ke AppUser Anda sendiri yang diwarisi dari IdentityUser):

@using Microsoft.AspNetCore.Identity

@using <namespaceWhereYouHaveYourAppUser>

@inject SignInManager<AppUser> SignInManager
@inject UserManager<AppUser> UserManager

@if (SignInManager.IsSignedIn(User)) {
    <form asp-area="Identity" asp-page="/Account/Logout" asp-route-returnUrl="@Url.Action("Index", "Home", new { area = "" })" 
          method="post" id="logoutForm" 
          class="form-inline my-2 my-lg-0">

        <ul class="nav navbar-nav ml-auto">
            <li class="nav-item">
                <a class="nav-link" asp-area="Identity" asp-page="/Account/Manage/Index" title="Manage">
                    Hello @((await UserManager.GetUserAsync(User)).ScreenName)!
                    <!-- Original code, shows Email-Address: @UserManager.GetUserName(User)! -->
                </a>
            </li>
            <li class="nav-item">
                <button type="submit" class="btn btn-link nav-item navbar-link nav-link">Logout</button>
            </li>
        </ul>

    </form>
} else {
    <ul class="navbar-nav ml-auto">
        <li class="nav-item"><a class="nav-link" asp-area="Identity" asp-page="/Account/Register">Register</a></li>
        <li class="nav-item"><a class="nav-link" asp-area="Identity" asp-page="/Account/Login">Login</a></li>
    </ul>
}
Jashan
sumber
2
Perlu dicatat bahwa GetUserAsync(User)akan meminta database untuk mengambil OrganizationId. Sebaliknya, solusi yang diterima akan memasukkan OrganizationId dalam klaim (mis. Cookie). Keuntungan menarik informasi ini dari database adalah bahwa orang dapat dipindahkan antar organisasi tanpa mengharuskan mereka keluar / masuk. Tentu saja, kerugiannya adalah membutuhkan permintaan basis data tambahan.
Matt
1

Dhaust memberikan cara yang baik untuk menambahkan properti ke kelas ApplicationUser. Melihat kode OP tampaknya mereka mungkin telah melakukan ini atau berada di jalur untuk melakukan itu. Pertanyaan itu bertanya

Bagaimana saya bisa mengambil properti OrganizationId dari pengguna yang saat ini masuk dari dalam pengontrol? Namun, OrganizationId bukan properti yang tersedia di User.Identity. Apakah saya perlu memperluas User.Identity untuk memasukkan properti OrganizationId?

Pawel memberikan cara untuk menambahkan metode ekstensi yang mengharuskan menggunakan pernyataan atau menambahkan namespace ke file web.config.

Namun, pertanyaannya bertanya apakah Anda "perlu" memperluas Pengguna. Identitas untuk menyertakan properti baru. Ada cara alternatif untuk mengakses properti tanpa memperpanjang User.Identity. Jika Anda mengikuti metode Knalpot Anda kemudian dapat menggunakan kode berikut di controller Anda untuk mengakses properti baru.

ApplicationDbContext db = new ApplicationDbContext();
var manager = new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(db));
var currentUser = manager.FindById(User.Identity.GetUserId());
var myNewProperty = currentUser.OrganizationId;
codeMethod
sumber
0

Saya juga telah menambahkan atau menambahkan kolom tambahan ke dalam tabel AspNetUsers saya. Ketika saya hanya ingin melihat data ini, saya menemukan banyak contoh seperti kode di atas dengan "Extensions" dll ... Ini benar-benar membuat saya kagum bahwa Anda harus menulis semua baris kode hanya untuk mendapatkan beberapa nilai dari pengguna saat ini.

Ternyata Anda bisa meminta tabel AspNetUsers seperti tabel lainnya:

 ApplicationDbContext db = new ApplicationDbContext();
 var user = db.Users.Where(x => x.UserName == User.Identity.Name).FirstOrDefault();
Anthony Griggs
sumber