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


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

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

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()

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

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;

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;

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

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;

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.


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


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();
    }
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.