MVC 5 Mengakses Data Pengguna Identitas Klaim

119

Saya sedang mengembangkan aplikasi web MVC 5 menggunakan pendekatan Entity Framework 5 Database First . Saya menggunakan OWIN untuk otentikasi Pengguna. Di bawah ini menunjukkan metode Login saya dalam Pengontrol Akun saya.

public ActionResult Login(LoginViewModel model, string returnUrl)
{
    if (ModelState.IsValid)
    {
        var user = _AccountService.VerifyPassword(model.UserName, model.Password, false);
        if (user != null)
        {
            var identity = new ClaimsIdentity(new[] { new Claim(ClaimTypes.Name, model.UserName), }, DefaultAuthenticationTypes.ApplicationCookie, ClaimTypes.Name, ClaimTypes.Role);

            identity.AddClaim(new Claim(ClaimTypes.Role, "guest"));
            identity.AddClaim(new Claim(ClaimTypes.GivenName, "A Person"));
            identity.AddClaim(new Claim(ClaimTypes.Sid, user.userID)); //OK to store userID here?

            AuthenticationManager.SignIn(new AuthenticationProperties
            {
                IsPersistent = model.RememberMe
            }, identity);

            return RedirectToAction("Index", "MyDashboard");
        }
        else
        {
            ModelState.AddModelError("", "Invalid username or password.");
        }
    }
    // If we got this far, something failed, redisplay form
    return View(model);
}

Seperti yang Anda lihat, saya sedang membuat ClaimsIdentity dan menambahkan beberapa klaim ke dalamnya, lalu meneruskannya ke OWIN menggunakan AuthenticationManager untuk masuk.

Masalah yang saya hadapi adalah saya tidak yakin bagaimana cara mengakses klaim di seluruh aplikasi saya, baik di Controllers atau di Razor Views.

Saya telah mencoba pendekatan yang tercantum dalam tutorial ini

http://brockallen.com/2013/10/24/a-primer-on-owin-cookie-authentication-middleware-for-the-asp-net-developer/

Misalnya, saya mencoba ini di kode Pengontrol saya dalam upaya untuk mendapatkan akses ke nilai yang diteruskan ke Klaim, namun, pengguna. Klaim sama dengan null

var ctx = HttpContext.GetOwinContext();
ClaimsPrincipal user = ctx.Authentication.User;
IEnumerable<Claim> claims = user.Claims;

Mungkin saya melewatkan sesuatu di sini.

MEMPERBARUI

Berdasarkan jawaban Darin, saya menambahkan kodenya tetapi tetap saja saya gagal melihat akses ke Klaim. Silakan lihat tangkapan layar di bawah ini yang menunjukkan apa yang saya lihat ketika mengarahkan kursor ke atas identitas.

masukkan deskripsi gambar di sini

tcode
sumber
Dapatkah Anda mengonfirmasi bahwa cookie dikirim kembali oleh browser? Mungkin setelan keamanan Anda menuntut SSL?
hak istimewa paling rendah
@leastprivilege Terima kasih, saya akan memeriksanya sekarang. Saya menemukan pertanyaan ini di Stackoverflow, stackoverflow.com/questions/20319118/… ini adalah masalah yang sama persis dengan yang saya alami, tetapi sayangnya tidak ada jawaban untuk itu :(
tcode
Bagaimana komponen OWIN Anda diinisialisasi?
Derek Van Cuyk
Saya baru-baru ini mengalami masalah seperti ini; Saya harap solusi ini membantu: stackoverflow.com/questions/34537475/…
Alexandru

Jawaban:

172

Coba ini:

[Authorize]
public ActionResult SomeAction()
{
    var identity = (ClaimsIdentity)User.Identity;
    IEnumerable<Claim> claims = identity.Claims;
    ...
}
Darin Dimitrov
sumber
Terima kasih atas bantuan Anda. Saya menggunakan jawaban yang Anda sarankan dalam Tindakan dalam Pengontrol untuk mencoba dan mengakses nilai Klaim, namun, identitas saya. Klaim masih NULL (lihat pertanyaan yang diperbarui dengan tangkapan layar). Ada ide lain? Saya menghargai bantuan Anda.
tcode
Tidak, maaf saya tidak punya ide lain. Ini selalu berhasil untuk saya.
Darin Dimitrov
Maaf, satu pertanyaan terakhir. Apakah saya perlu membuat kelas ClaimsAuthenticationManager dan Application_PostAuthenticateRequest () khusus saya sendiri di Global.asax seperti ini dotnetcodr.com/2013/02/25/… sebelum kode saya di atas berfungsi? Terima kasih lagi.
tcode
7
Sampai Anda Mengotorisasi untuk pertama kalinya, Anda tidak akan memiliki akses ke ini sampai setelah metode login Anda, itulah sebabnya OP tidak melihatnya pada saat itu. Anda harus memuat secara manual saat ini jika Anda menginginkannya dalam metode Login.
Adam Tuliper - MSFT
Saya bekerja dengan inti asp.net dan mencari cara untuk mendapatkan gambar profil LinkedIn selama lebih dari 2 jam. Saya terjebak, saya lelah, saya ingin menyerah sampai saya melihat jawaban Anda. Saya ingin mengucapkan terima kasih jutaan kali ... +1
Vayne
36

Anda juga bisa melakukan ini:

//Get the current claims principal
var identity = (ClaimsPrincipal)Thread.CurrentPrincipal;
var claims = identity.Claims;

Memperbarui

Untuk memberikan penjelasan lebih lanjut sesuai komentar.

Jika Anda membuat pengguna dalam sistem Anda sebagai berikut:

UserManager<applicationuser> userManager = new UserManager<applicationuser>(new UserStore<applicationuser>(new SecurityContext()));
ClaimsIdentity identity = userManager.CreateIdentity(user, DefaultAuthenticationTypes.ApplicationCookie);

Anda akan secara otomatis memiliki beberapa Klaim yang terkait dengan Identitas Anda.

Untuk menambahkan klaim yang disesuaikan setelah pengguna mengautentikasi, Anda dapat melakukan ini sebagai berikut:

var user = userManager.Find(userName, password);
identity.AddClaim(new Claim(ClaimTypes.Email, user.Email));

Klaim tersebut dapat dibaca kembali seperti yang telah dijawab oleh Darin di atas atau seperti yang saya lakukan.

Klaim dipertahankan ketika Anda menelepon di bawah ini meneruskan identitas di:

AuthenticationManager.SignIn(new AuthenticationProperties() { IsPersistent = persistCookie }, identity);
hutchonoid.dll
sumber
Terima kasih, tapi tetap saja ini tidak berhasil untuk saya. Dapatkah Anda melihat pertanyaan terbaru saya? Juga, satu pertanyaan terakhir. Apakah saya perlu membuat kelas ClaimsAuthenticationManager dan Application_PostAuthenticateRequest () khusus saya sendiri di Global.asax seperti ini dotnetcodr.com/2013/02/25/… sebelum kode saya di atas berfungsi? Terima kasih sekali lagi atas bantuan Anda.
tcode
Hai, saya akan melihat ketika saya kembali menggunakan PC.
hutchonoid
terima kasih, sangat menghargainya. Pada tahap di mana ini mulai membuat saya
bingung
@tgriffiths Hai, saya telah menambahkan pembaruan untuk Anda. Semoga memberikan sedikit info lebih lanjut. Semoga berhasil. :)
hutchonoid
sayangnya saya tidak menggunakan built in Entity Framework Code First, misalnya UserManager dll. Tapi terima kasih atas masukan Anda. Bersulang.
tcode
30

Saya membuat kelas tambahan saya sendiri untuk melihat apa yang saya butuhkan, jadi ketika saya perlu ke controller atau View saya, saya hanya menambahkan use ke namespace saya seperti ini:

public static class UserExtended
{
    public static string GetFullName(this IPrincipal user)
    {
        var claim = ((ClaimsIdentity)user.Identity).FindFirst(ClaimTypes.Name);
        return claim == null ? null : claim.Value;
    }
    public static string GetAddress(this IPrincipal user)
    {
        var claim = ((ClaimsIdentity)user.Identity).FindFirst(ClaimTypes.StreetAddress);
        return claim == null ? null : claim.Value;
    }
    public ....
    {
      .....
    }
}

Di pengontrol saya:

using XXX.CodeHelpers.Extended;

var claimAddress = User.GetAddress();

Di pisau cukur saya:

@using DinexWebSeller.CodeHelpers.Extended;

@User.GetFullName()
Yesus Padilla
sumber
8
return claim?.Value;karena mengapa tidak
Halter
17

Ini adalah alternatif jika Anda tidak ingin menggunakan klaim sepanjang waktu. Lihat tutorial ini oleh Ben Foster.

public class AppUser : ClaimsPrincipal
{
    public AppUser(ClaimsPrincipal principal)
        : base(principal)
    {
    }

    public string Name
    {
        get
        {
            return this.FindFirst(ClaimTypes.Name).Value;
        } 
    }

}

Kemudian Anda dapat menambahkan pengontrol dasar.

public abstract class AppController : Controller
{       
    public AppUser CurrentUser
    {
        get
        {
            return new AppUser(this.User as ClaimsPrincipal);
        }
    }
}

Di pengontrol Anda, Anda akan melakukan:

public class HomeController : AppController
{
    public ActionResult Index()
    {
        ViewBag.Name = CurrentUser.Name;
        return View();
    }
}
Quentin
sumber
12

Untuk lebih lanjut menyentuh jawaban Darwin, Anda dapat memperoleh klaim spesifik Anda dengan menggunakan metode FindFirst :

var identity = (ClaimsIdentity)User.Identity;
var role = identity.FindFirst(ClaimTypes.Role).Value;
Eric Bridges
sumber
Atau yang ini terlalu string myValue = identity.FindFirstValue ("MyClaimType");
Juan Carlos Puerto
Apa yang terjadi jika FindFirst tidak menemukan klaim dengan jenis "peran"? pengecualian nol?
Phil
10

Anda juga bisa melakukan ini.

IEnumerable<Claim> claims = ClaimsPrincipal.Current.Claims;
angularsen
sumber
8

Ingatlah bahwa untuk menanyakan IEnumerable Anda perlu mereferensikan system.linq.
Ini akan memberi Anda objek ekstensi yang perlu dilakukan:

CaimsList.FirstOrDefault(x=>x.Type =="variableName").toString();
Marc Sedote Sossou
sumber
7

versi jawaban @Rosdi Kasim'd yang paling singkat dan disederhanakan adalah

string claimvalue = ((System.Security.Claims.ClaimsIdentity)User.Identity).
    FindFirst("claimname").Value;

Claimname adalah klaim yang ingin anda ambil, yaitu jika anda mencari klaim "StreedAddress" maka jawaban di atas akan seperti ini

string claimvalue = ((System.Security.Claims.ClaimsIdentity)User.Identity).
    FindFirst("StreedAddress").Value;
noman zafar
sumber
memberikan contoh "nilai klaim" menghemat waktu saya. Terima kasih
Nour Lababidi
6
Request.GetOwinContext().Authentication.User.Claims

Namun lebih baik menambahkan klaim di dalam metode "GenerateUserIdentityAsync", terutama jika regenerateIdentity di Startup.Auth.cs diaktifkan.

Basil Banbouk
sumber
Itu GenerateUserIdentityAsyncadalah saran yang luar biasa, saya benar-benar telah mengabaikannya. Terima kasih banyak, Basil.
timmi4sa
2

Menurut kelas ControllerBase, Anda bisa mendapatkan klaim untuk pengguna yang menjalankan tindakan.

masukkan deskripsi gambar di sini

inilah cara Anda melakukannya dalam 1 baris.

var claims = User.Claims.ToList();
conterio
sumber
1
var claim = User.Claims.FirstOrDefault(c => c.Type == "claim type here");
Felipe Deveza
sumber
0

Saya menggunakannya seperti itu di pengontrol dasar saya. Hanya berbagi untuk siap digunakan.

    public string GetCurrentUserEmail() {
        var identity = (ClaimsIdentity)User.Identity;
        IEnumerable<Claim> claims = identity.Claims;
        var email = claims.Where(c => c.Type == ClaimTypes.Email).ToList();
        return email[0].Value.ToString();
    }

    public string GetCurrentUserRole()
    {
        var identity = (ClaimsIdentity)User.Identity;
        IEnumerable<Claim> claims = identity.Claims;
        var role = claims.Where(c => c.Type == ClaimTypes.Role).ToList();
        return role[0].Value.ToString();
    }
BM
sumber