MVC 5 Access Claims Identity User Data

119

Opracowuję aplikację internetową MVC 5 przy użyciu metody Entity Framework 5 Database First . Używam OWIN do uwierzytelniania użytkowników. Poniżej przedstawiono moją metodę logowania w kontrolerze konta.

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

Jak widać, tworzę ClaimsIdentity i dodaję do niego kilka oświadczeń, a następnie przekazuję je do OWIN przy użyciu AuthenticationManager w celu wykonania logowania.

Problem, który mam, polega na tym, że nie jestem pewien, jak uzyskać dostęp do oświadczeń w pozostałej części mojej aplikacji, czy to w kontrolerach, czy w widokach Razor.

Wypróbowałem podejście wymienione w tym samouczku

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

Na przykład próbowałem tego w moim kodzie kontrolera, próbując uzyskać dostęp do wartości przekazanych do oświadczeń, jednak użytkownik.

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

Może czegoś mi tu brakuje.

AKTUALIZACJA

Bazując na odpowiedzi Darina, dodałem jego kod, ale nadal nie widzę dostępu do roszczeń. Poniżej zrzut ekranu pokazujący, co widzę po najechaniu kursorem myszy na tożsamość.

wprowadź opis obrazu tutaj

tcode
źródło
Czy możesz potwierdzić, że plik cookie jest odesłany przez przeglądarkę? Może twoje ustawienia bezpieczeństwa wymagają SSL?
najmniejprivilege
@leastprivilege Dzięki, przyjrzę się temu teraz. Znalazłem to pytanie na Stackoverflow, stackoverflow.com/questions/20319118/ ... to jest dokładnie ten sam problem, który mam, ale niestety brak odpowiedzi :(
tcode
Jak są inicjalizowane komponenty OWIN?
Derek Van Cuyk
Niedawno miałem taki problem; Mam nadzieję, że to rozwiązanie pomoże: stackoverflow.com/questions/34537475/ ...
Alexandru,

Odpowiedzi:

172

Spróbuj tego:

[Authorize]
public ActionResult SomeAction()
{
    var identity = (ClaimsIdentity)User.Identity;
    IEnumerable<Claim> claims = identity.Claims;
    ...
}
Darin Dimitrov
źródło
Dzięki za pomoc. Skorzystałem z Twojej sugerowanej odpowiedzi w akcji w kontrolerze, aby spróbować uzyskać dostęp do wartości Claims, jednak ja tożsamość. Roszczenia nadal mają wartość NULL (zobacz zaktualizowane pytanie ze zrzutem ekranu). Jakieś inne pomysły? Doceniam twoją pomoc.
tcode
Nie, przepraszam, nie mam innych pomysłów. To zawsze działało dla mnie.
Darin Dimitrov
Przepraszam, ostatnie pytanie. Czy muszę utworzyć własną niestandardową klasę ClaimsAuthenticationManager i Application_PostAuthenticateRequest () w Global.asax, tak jak to dotnetcodr.com/2013/02/25/ ... zanim mój kod powyżej zadziała? Dzięki jeszcze raz.
tcode
7
Dopóki nie autoryzujesz po raz pierwszy, nie będziesz mieć do niego dostępu, dopóki nie zakończysz metody logowania, dlatego OP nie widzi tego w tym czasie. Musisz w tym momencie załadować ręcznie, jeśli chcesz to w metodzie logowania.
Adam Tuliper - MSFT
Pracuję z rdzeniem asp.net i szukam sposobu na uzyskanie zdjęcia profilowego na LinkedIn przez ponad 2 godziny. Utknąłem, jestem zmęczony, chcę się poddać, dopóki nie zobaczę twojej odpowiedzi. Chcę podziękować milion razy ... +1
Vayne
36

Możesz też to zrobić:

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

Aktualizacja

Aby udzielić dalszych wyjaśnień zgodnie z komentarzami.

Jeśli tworzysz użytkowników w swoim systemie w następujący sposób:

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

Powinieneś automatycznie wypełnić niektóre roszczenia dotyczące Twojej tożsamości.

Aby dodać niestandardowe oświadczenia po uwierzytelnieniu użytkownika, możesz to zrobić w następujący sposób:

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

Twierdzenia można odczytać tak, jak Darin odpowiedział powyżej lub tak, jak ja.

Roszczenia są utrwalane, gdy wywołujesz poniżej przekazanie tożsamości w:

AuthenticationManager.SignIn(new AuthenticationProperties() { IsPersistent = persistCookie }, identity);
hutchonoid
źródło
Dzięki, ale to nadal nie działa dla mnie. Czy widzisz moje zaktualizowane pytanie? I ostatnie pytanie. Czy muszę utworzyć własną niestandardową klasę ClaimsAuthenticationManager i Application_PostAuthenticateRequest () w Global.asax, na przykład w ten sposób dotnetcodr.com/2013/02/25/ ... zanim mój kod powyżej zadziała? Jeszcze raz dziękuję za pomoc.
tcode
Cześć, przyjrzę się, kiedy wrócę na komputer.
hutchonoid
dzięki, naprawdę to doceniam. Na etapie, na którym zaczyna mnie to pękać :)
tcode
@tgriffiths Cześć, dodałem dla Ciebie aktualizację. Mam nadzieję, że podam trochę więcej informacji. Powodzenia. :)
hutchonoid
niestety nie używam wbudowanego w Entity Framework Code First, np. UserManager itp. Ale dzięki za wkład. Twoje zdrowie.
tcode
30

Tworzę własną rozszerzoną klasę, aby zobaczyć, czego potrzebuję, więc kiedy potrzebuję do mojego kontrolera lub widoku, dodaję użycie do mojej przestrzeni nazw, coś takiego:

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

W moim kontrolerze:

using XXX.CodeHelpers.Extended;

var claimAddress = User.GetAddress();

W mojej brzytwie:

@using DinexWebSeller.CodeHelpers.Extended;

@User.GetFullName()
Jesus Padilla
źródło
8
return claim?.Value;bo czemu nie
Halter
17

To alternatywa, jeśli nie chcesz przez cały czas używać roszczeń. Spójrz na ten samouczek autorstwa Bena Fostera.

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

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

}

Następnie możesz dodać kontroler podstawowy.

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

W swoim kontrolerze zrobiłbyś:

public class HomeController : AppController
{
    public ActionResult Index()
    {
        ViewBag.Name = CurrentUser.Name;
        return View();
    }
}
Quentin
źródło
12

Aby dokładniej dotknąć odpowiedzi Darina, możesz przejść do swoich konkretnych roszczeń, używając metody FindFirst :

var identity = (ClaimsIdentity)User.Identity;
var role = identity.FindFirst(ClaimTypes.Role).Value;
Eric Bridges
źródło
Lub ten też string myValue = identity.FindFirstValue ("MyClaimType");
Juan Carlos Puerto
Co się stanie, jeśli FindFirst nie znajdzie żadnych roszczeń o typie „rola”? zerowy wyjątek?
Phil
10

Ty też możesz to zrobić.

IEnumerable<Claim> claims = ClaimsPrincipal.Current.Claims;
angularsen
źródło
8

Pamiętaj, że aby wysłać zapytanie do IEnumerable, musisz odwołać się do system.linq.
To da ci obiekt rozszerzenia potrzebny do:

CaimsList.FirstOrDefault(x=>x.Type =="variableName").toString();
Marc Sedote Sossou
źródło
7

Najkrótsza i uproszczona wersja odpowiedzi @Rosdi Kasim to

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

Claimname to roszczenie, które chcesz odzyskać tj. jeśli szukasz roszczenia "StreedAddress" to powyższa odpowiedź będzie wyglądać następująco

string claimvalue = ((System.Security.Claims.ClaimsIdentity)User.Identity).
    FindFirst("StreedAddress").Value;
noman zafar
źródło
podanie przykładu „claimvalue” oszczędza mój czas. Dziękuję
Nour Lababidi
6
Request.GetOwinContext().Authentication.User.Claims

Jednak lepiej jest dodać oświadczenia wewnątrz metody „GenerateUserIdentityAsync”, zwłaszcza jeśli włączona jest opcja regenerateIdentity w pliku Startup.Auth.cs.

Basil Banbouk
źródło
To GenerateUserIdentityAsyncbyła niesamowita sugestia, całkowicie ją przeoczyłem. Wielkie dzięki, Bazyli.
timmi4sa
2

Zgodnie z klasą ControllerBase można uzyskać oświadczenia dla użytkownika wykonującego akcję.

wprowadź opis obrazu tutaj

oto jak możesz to zrobić w 1 linii.

var claims = User.Claims.ToList();
conterio
źródło
1
var claim = User.Claims.FirstOrDefault(c => c.Type == "claim type here");
Felipe Deveza
źródło
0

Użyłem go w ten sposób w moim kontrolerze podstawowym. Po prostu udostępniaj gotowe do użycia.

    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
źródło