MVC 5 Access Claims Identity-Benutzerdaten

119

Ich entwickle eine MVC 5- Webanwendung mit dem Entity Framework 5 Database First- Ansatz. Ich verwende OWIN zur Authentifizierung von Benutzern. Unten sehen Sie meine Anmeldemethode in meinem Account Controller.

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

Wie Sie sehen, erstelle ich eine ClaimsIdentity , füge mehrere Claims hinzu und übergebe sie dann mit dem AuthenticationManager an OWIN , um die Anmeldung durchzuführen.

Das Problem, das ich habe, ist, dass ich nicht sicher bin, wie ich auf die Ansprüche im Rest meiner Anwendung zugreifen soll, weder in Controllern noch in Razor Views.

Ich hatte den in diesem Tutorial aufgeführten Ansatz ausprobiert

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

Ich habe dies beispielsweise in meinem Controller-Code versucht, um Zugriff auf die an die Ansprüche übergebenen Werte zu erhalten. Der Benutzer ist jedoch gleich null

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

Vielleicht fehlt mir hier etwas.

AKTUALISIEREN

Basierend auf Darins Antwort habe ich seinen Code hinzugefügt, aber ich sehe immer noch keinen Zugriff auf die Ansprüche. Bitte sehen Sie den Screenshot unten, der zeigt, was ich sehe, wenn ich mit der Maus über die Identität schwebe.

Geben Sie hier die Bildbeschreibung ein

tcode
quelle
Können Sie bestätigen, dass der Cookie vom Browser zurückgesendet wird? Möglicherweise erfordern Ihre Sicherheitseinstellungen SSL?
geringstes Privileg
@leastprivilege Danke, ich werde das jetzt untersuchen. Ich habe diese Frage auf Stackoverflow gefunden, stackoverflow.com/questions/20319118/… es ist genau das gleiche Problem, das ich habe, aber leider keine Antwort darauf :(
tcode
Wie werden Ihre OWIN-Komponenten initialisiert?
Derek Van Cuyk
Ich hatte kürzlich ein Problem wie dieses; Ich hoffe, diese Lösung hilft: stackoverflow.com/questions/34537475/…
Alexandru

Antworten:

171

Versuche dies:

[Authorize]
public ActionResult SomeAction()
{
    var identity = (ClaimsIdentity)User.Identity;
    IEnumerable<Claim> claims = identity.Claims;
    ...
}
Darin Dimitrov
quelle
Danke für Ihre Hilfe. Ich habe Ihre vorgeschlagene Antwort in einer Aktion innerhalb eines Controllers verwendet, um zu versuchen, auf die Werte für Ansprüche zuzugreifen. Ich bin jedoch identisch. Ansprüche sind immer noch NULL (siehe aktualisierte Frage mit Screenshot). Irgendwelche anderen Ideen? Ich schätze Ihre Hilfe.
Tcode
Nein, tut mir leid, ich habe keine anderen Ideen. Das hat bei mir immer funktioniert.
Darin Dimitrov
Entschuldigung, eine letzte Frage. Muss ich meine eigene ClaimsAuthenticationManager-Klasse und Application_PostAuthenticateRequest () in Global.asax wie diese dotnetcodr.com/2013/02/25/… erstellen, bevor mein obiger Code funktioniert? Danke noch einmal.
Tcode
7
Bis Sie zum ersten Mal autorisieren, haben Sie erst nach Ihrer Anmeldemethode Zugriff darauf, weshalb das OP dies zu diesem Zeitpunkt nicht sieht. Sie müssen zu diesem Zeitpunkt manuell laden, wenn Sie dies in der Anmeldemethode wünschen.
Adam Tuliper - MSFT
Ich arbeite mit asp.net core und suche nach einer Möglichkeit, in mehr als 2 Stunden ein LinkedIn-Profilbild zu erhalten. Ich stecke fest, ich bin müde, ich möchte aufgeben, bis ich deine Antwort sehe. Ich möchte mich millionenfach bedanken ... +1
Vayne
36

Sie können dies auch tun:

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

Aktualisieren

Weitere Erläuterungen gemäß Kommentaren.

Wenn Sie Benutzer in Ihrem System wie folgt erstellen:

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

Es sollten automatisch einige Ansprüche in Bezug auf Ihre Identität ausgefüllt werden.

Um benutzerdefinierte Ansprüche hinzuzufügen, nachdem sich ein Benutzer authentifiziert hat, gehen Sie wie folgt vor:

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

Die Behauptungen können wieder vorgelesen werden, wie Darin oben geantwortet hat oder wie ich.

Die Ansprüche bleiben bestehen, wenn Sie unten anrufen und die Identität in übergeben:

AuthenticationManager.SignIn(new AuthenticationProperties() { IsPersistent = persistCookie }, identity);
Hutchonoid
quelle
Danke, aber das funktioniert bei mir immer noch nicht. Können Sie meine aktualisierte Frage sehen? Auch eine letzte Frage. Muss ich meine eigene ClaimsAuthenticationManager-Klasse und Application_PostAuthenticateRequest () in Global.asax wie diese dotnetcodr.com/2013/02/25/… erstellen, bevor mein obiger Code funktioniert? Danke nochmal für deine Hilfe.
Tcode
Hallo, ich werde einen Blick darauf werfen, wenn ich wieder auf einem PC bin.
Hutchonoid
danke, schätze es wirklich. In einer Phase, in der mich das
langsam verrückt macht
@tgriffiths Hallo, ich habe ein Update für dich hinzugefügt. Hoffentlich ein bisschen mehr Infos. Viel Glück. :)
Hutchonoid
Leider verwende ich nicht zuerst den eingebauten Entity Framework Code, z. B. UserManager usw. Aber danke für Ihre Eingabe. Prost.
Tcode
30

Ich erstelle meine eigene erweiterte Klasse, um zu sehen, was ich brauche. Wenn ich sie in meinem Controller oder meiner Ansicht benötige, füge ich meinem Namespace nur die Verwendung hinzu:

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

In meinem Controller:

using XXX.CodeHelpers.Extended;

var claimAddress = User.GetAddress();

In meinem Rasiermesser:

@using DinexWebSeller.CodeHelpers.Extended;

@User.GetFullName()
Jesus Padilla
quelle
8
return claim?.Value;denn warum nicht
Halter
17

Dies ist eine Alternative, wenn Sie Ansprüche nicht ständig verwenden möchten. Schauen Sie sich dieses Tutorial von Ben Foster an.

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

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

}

Dann können Sie einen Basis-Controller hinzufügen.

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

In Ihrem Controller würden Sie Folgendes tun:

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

Um Darins Antwort weiter zu berühren, können Sie mithilfe der FindFirst- Methode zu Ihren spezifischen Ansprüchen gelangen :

var identity = (ClaimsIdentity)User.Identity;
var role = identity.FindFirst(ClaimTypes.Role).Value;
Eric Bridges
quelle
Oder auch dieser String myValue = identity.FindFirstValue ("MyClaimType");
Juan Carlos Puerto
Was passiert, wenn FindFirst keine Ansprüche mit dem Typ "Rolle" findet? Null Ausnahme?
Phil
10

Sie können dies auch tun.

IEnumerable<Claim> claims = ClaimsPrincipal.Current.Claims;
eckig
quelle
8

Denken Sie daran, dass Sie zum Abfragen der IEnumerable auf system.linq verweisen müssen.
Sie erhalten das erforderliche Erweiterungsobjekt:

CaimsList.FirstOrDefault(x=>x.Type =="variableName").toString();
Marc Sedote Sossou
quelle
6
Request.GetOwinContext().Authentication.User.Claims

Es ist jedoch besser, die Ansprüche innerhalb der Methode "GenerateUserIdentityAsync" hinzuzufügen, insbesondere wenn regenerateIdentity in Startup.Auth.cs aktiviert ist.

Basil Banbouk
quelle
Das GenerateUserIdentityAsyncwar ein großartiger Vorschlag, ich habe ihn total übersehen. Vielen Dank Basil.
timmi4sa
6

Die kürzeste und vereinfachte Version der Antwort von @Rosdi Kasim ist

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

Claimname ist der Anspruch, den Sie abrufen möchten, dh wenn Sie nach dem Anspruch "StreedAddress" suchen, lautet die obige Antwort wie folgt

string claimvalue = ((System.Security.Claims.ClaimsIdentity)User.Identity).
    FindFirst("StreedAddress").Value;
Noman Zafar
quelle
Durch die Angabe des Beispiels "Anspruchswert" konnte ich Zeit sparen. Vielen Dank
Nour Lababidi
2

Gemäß der ControllerBase-Klasse können Sie die Ansprüche für den Benutzer abrufen, der die Aktion ausführt.

Geben Sie hier die Bildbeschreibung ein

Hier erfahren Sie, wie Sie dies in einer Zeile tun können.

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

Ich habe es so in meinem Basis-Controller verwendet. Einfach teilen, um sofort einsatzbereit zu sein.

    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
quelle