ASP.NET Core Identity - aktuellen Benutzer abrufen

81

Um den aktuell in MVC5 angemeldeten Benutzer zu erhalten, mussten wir lediglich Folgendes tun:

using Microsoft.AspNet.Identity;
[Authorize]
public IHttpActionResult DoSomething() {
    string currentUserId = User.Identity.GetUserId();
}

Mit ASP.NET Core dachte ich, dass dies funktionieren sollte, aber es gibt einen Fehler.

using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Http;

private readonly UserManager<ApplicationUser> _userManager;
[HttpPost]
[Authorize]
public async Task<IActionResult> StartSession() {
    var curUser = await _userManager.GetUserAsync(HttpContext.User);
}

Irgendwelche Ideen?

EDIT: Gerardos Antwort ist auf dem richtigen Weg, aber um die tatsächliche "ID" des Benutzers zu erhalten, scheint dies zu funktionieren:

ClaimsPrincipal currentUser = this.User;
var currentUserID = currentUser.FindFirst(ClaimTypes.NameIdentifier).Value;
jbraun
quelle
Was genau ist deine Frage?
Gerardo Grignoli
Sie brauchen nur die ID? Ich habe meine Antwort bearbeitet, um hinzuzufügen, wie man sie mit dem ausgefalleneren _userManager.GetUserId (Benutzer)
Gerardo Grignoli
Ja, hauptsächlich benötige ich die ID aus der AspNetUsers-Tabelle oder aus der Sitzung. currentUser.FindFirst (ClaimTypes.NameIdentifier) ​​.Value gibt die ID mithilfe von Claims an. Es funktioniert auch mit UserManager! Danke Gerardo! Ist ein Weg effizienter als der andere?
Jbraun
UserManager führt intern .FindFirst (ClaimTypes.NameIdentifier) ​​aus. In Bezug auf die Leistung ist es also genauso. Ich bevorzuge die Usermanager-Kapselung, um das Lesen zu erleichtern. Auf der anderen Seite .GetUserAsync()ist langsamer, weil es in die DB geht.
Gerardo Grignoli
Ich stimme Gerardo vollkommen zu. Danke !
Jbraun

Antworten:

138

Angenommen, Ihr Code befindet sich in einem MVC-Controller:

public class MyController : Microsoft.AspNetCore.Mvc.Controller

Von der ControllerBasisklasse können Sie die IClaimsPrincipalvon der UserEigenschaft erhalten

System.Security.Claims.ClaimsPrincipal currentUser = this.User;

Sie können die Ansprüche direkt überprüfen (ohne einen Roundtrip zur Datenbank):

bool IsAdmin = currentUser.IsInRole("Admin");
var id = _userManager.GetUserId(User); // Get user id:

Andere Felder können von der Benutzerentität der Datenbank abgerufen werden:

  1. Holen Sie sich den Benutzermanager mithilfe der Abhängigkeitsinjektion

    private UserManager<ApplicationUser> _userManager;
    
    //class constructor
    public MyController(UserManager<ApplicationUser> userManager)
    {
        _userManager = userManager;
    }
    
  2. Und benutze es:

    var user = await _userManager.GetUserAsync(User);
    var email = user.Email;
    
Gerardo Grignoli
quelle
4
Und wenn ich in einer Serviceklasse und nicht im Controller bin, wo habe ich dann nicht die Benutzereigenschaft? Wie erhalte ich das AppUser-Objekt des authentifizierten Benutzers?
Kirsten
13
@Kirsten Sie können eine IHttpContextAccessormit Abhängigkeitsinjektion empfangen und dann die Useraus dem http-Kontext abrufen . Fügen Sie einfach diesen Parameter zum Serviceklassenkonstruktor hinzu : IHttpContextAccessor contextAccessor. Halten Sie den Accessor auf einem Feld _contextAccessorund holen Sie ihn dann über die Servicemethoden ab _contextAccessor.HttpContext.User.
Gerardo Grignoli
5
@Kirsten Wenn Sie dies tun, wird Ihr Dienst mit ASP.NET Core gekoppelt, sodass er in anderen Szenarien nicht verwendet werden kann. Dies kann für Sie akzeptabel sein oder nicht. Ich werde auch in Betracht ziehen, beim Methodenaufruf genau das zu übergeben, was Sie möchten.
Gerardo Grignoli
2
Sie haben mich auf den richtigen Weg gebracht: Ich habe einen IUserService mit GetUser und IsAuthenticated erstellt. In meiner ASP.NET Core App habe ich einen Implementierungsbenutzerservice, der IHttpContextAccessor und den UserManager <ApplicationUser> verwendet. Mein userService ist also mit ASP.NET Core gekoppelt, meine anderen Dienste nicht.
Kirsten
2
Nein, sie gehen nicht in die Datenbank. Beim Überprüfen der Quelle wird der UserStore nicht verwendet. github.com/dotnet/corefx/blob/master/src/System.Security.Claims/… github.com/aspnet/Identity/blob/…
Gerardo Grignoli
31

Wenn Sie Bearing Token Auth verwenden, geben die obigen Beispiele keinen Anwendungsbenutzer zurück.

Verwenden Sie stattdessen Folgendes:

ClaimsPrincipal currentUser = this.User;
var currentUserName = currentUser.FindFirst(ClaimTypes.NameIdentifier).Value;
ApplicationUser user = await _userManager.FindByNameAsync(currentUserName);

Dies funktioniert in apsnetcore 2.0. Habe es in früheren Versionen nicht versucht.

Greg Gum
quelle
Meine Token werden validiert, aber der Auftraggeber und die Ansprüche werden nicht festgelegt. Dies geschieht in Dotnet Core 2.1 und 2.2
ps2goat
@ ps2goat, ich würde eine neue Frage erstellen, um dies zu beheben, und den Code einschließen, der das Token erstellt.
Greg Gum
1
Ich habe es herausgefunden. Wir haben kein Standardschema, da wir 5 verschiedene Schemata haben. Ich musste eine Middleware hinzufügen, um das Token zu dekodieren und die Ansprüche dem aktuellen Benutzer zuzuweisen. Ich sollte wahrscheinlich eine Frage schreiben und sie für andere beantworten, da meine Situation nur sehr wenig Inhalt hat.
ps2goat
1
ClaimTypes.Name erhält den Benutzernamen, nach dem ich gesucht habe, anstelle der ID. ClaimTypes.NameIdentifier ruft die ID des Benutzers ab. Nur für den Fall, dass jemand dies sieht und sich fragt, wie er den Benutzernamen erhalten kann. Danke für deine Antwort Greg!
Matthew Zourelias
7

Für den Kontext habe ich ein Projekt mit der ASP.NET Core 2-Webanwendungsvorlage erstellt. Wählen Sie dann die Webanwendung (MVC) aus, klicken Sie auf die Schaltfläche Authentifizierung ändern und wählen Sie Einzelbenutzerkonten aus.

Aus dieser Vorlage wird eine Menge Infrastruktur für Sie aufgebaut. Suchen Sie die ManageControllerim Ordner Controller.

Für diesen ManageControllerKlassenkonstruktor muss diese UserManager-Variable ausgefüllt sein:

private readonly UserManager<ApplicationUser> _userManager;

Schauen Sie sich dann die Indexmethode [HttpPost] in dieser Klasse an. Sie erhalten den aktuellen Benutzer auf diese Weise:

var user = await _userManager.GetUserAsync(User);

Als Bonus-Hinweis möchten Sie hier alle benutzerdefinierten Felder des Benutzerprofils aktualisieren, das Sie der AspNetUsers-Tabelle hinzugefügt haben. Fügen Sie die Felder zur Ansicht hinzu und senden Sie diese Werte an das IndexViewModel, das dann an diese Post-Methode gesendet wird. Ich habe diesen Code nach der Standardlogik hinzugefügt, um die E-Mail-Adresse und die Telefonnummer festzulegen:

user.FirstName = model.FirstName;
user.LastName = model.LastName;
user.Address1 = model.Address1;
user.Address2 = model.Address2;
user.City = model.City;
user.State = model.State;
user.Zip = model.Zip;
user.Company = model.Company;
user.Country = model.Country;
user.SetDisplayName();
user.SetProfileID();

_dbContext.Attach(user).State = EntityState.Modified;
_dbContext.SaveChanges();
Joel Palmer
quelle
5

In .NET Core 2.0 ist der Benutzer bereits als Teil des zugrunde liegenden geerbten Controllers vorhanden. Verwenden Sie den Benutzer einfach wie gewohnt oder geben Sie ihn an einen beliebigen Repository-Code weiter.

[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme, Policy = "TENANT")]
[HttpGet("issue-type-selection"), Produces("application/json")]
public async Task<IActionResult> IssueTypeSelection()
{
    try
    {
        return new ObjectResult(await _item.IssueTypeSelection(User));
    }
    catch (ExceptionNotFound)
    {
        Response.StatusCode = (int)HttpStatusCode.BadRequest;
        return Json(new
        {
            error = "invalid_grant",
            error_description = "Item Not Found"
        });
    }
}

Hierher erbt es es

#region Assembly Microsoft.AspNetCore.Mvc.Core, Version=2.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60
// C:\Users\BhailDa\.nuget\packages\microsoft.aspnetcore.mvc.core\2.0.0\lib\netstandard2.0\Microsoft.AspNetCore.Mvc.Core.dll
#endregion

using System;
using System.IO;
using System.Linq.Expressions;
using System.Runtime.CompilerServices;
using System.Security.Claims;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
using Microsoft.AspNetCore.Routing;
using Microsoft.Net.Http.Headers;

namespace Microsoft.AspNetCore.Mvc
{
    //
    // Summary:
    //     A base class for an MVC controller without view support.
    [Controller]
    public abstract class ControllerBase
    {
        protected ControllerBase();

        //
        // Summary:
        //     Gets the System.Security.Claims.ClaimsPrincipal for user associated with the
        //     executing action.
        public ClaimsPrincipal User { get; }
Bhail
quelle
2

Nur wenn jemand interessiert ist, hat das bei mir funktioniert. Ich habe eine benutzerdefinierte Identität, die int für einen Primärschlüssel verwendet, sodass ich die GetUserAsync-Methode überschrieben habe

GetUserAsync überschreiben

public override Task<User> GetUserAsync(ClaimsPrincipal principal)
{
    var userId = GetUserId(principal);
    return FindByNameAsync(userId);
}

Identitätsbenutzer abrufen

var user = await _userManager.GetUserAsync(User);

Wenn Sie einen regulären Guid-Primärschlüssel verwenden, müssen Sie GetUserAsync nicht überschreiben. Dies setzt voraus, dass Ihr Token korrekt konfiguriert ist.

public async Task<string> GenerateTokenAsync(string email)
{
    var user = await _userManager.FindByEmailAsync(email);
    var tokenHandler = new JwtSecurityTokenHandler();
    var key = Encoding.ASCII.GetBytes(_tokenProviderOptions.SecretKey);

    var userRoles = await _userManager.GetRolesAsync(user);
    var roles = userRoles.Select(o => new Claim(ClaimTypes.Role, o));

    var claims = new[]
    {
        new Claim(JwtRegisteredClaimNames.Sub, user.UserName),
        new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
        new Claim(JwtRegisteredClaimNames.Iat, DateTime.UtcNow.ToString(CultureInfo.CurrentCulture)),
        new Claim(JwtRegisteredClaimNames.GivenName, user.FirstName),
        new Claim(JwtRegisteredClaimNames.FamilyName, user.LastName),
        new Claim(JwtRegisteredClaimNames.Email, user.Email),
    }
    .Union(roles);

    var tokenDescriptor = new SecurityTokenDescriptor
    {
        Subject = new ClaimsIdentity(claims),
        Expires = DateTime.UtcNow.AddHours(_tokenProviderOptions.Expires),
        SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature)
    };

    var token = tokenHandler.CreateToken(tokenDescriptor);

    return Task.FromResult(new JwtSecurityTokenHandler().WriteToken(token)).Result;
}
Dblock247
quelle
1
private readonly UserManager<AppUser> _userManager;

 public AccountsController(UserManager<AppUser> userManager)
 {
            _userManager = userManager;
 }

[Authorize(Policy = "ApiUser")]
[HttpGet("api/accounts/GetProfile", Name = "GetProfile")]
public async Task<IActionResult> GetProfile()
{
   var userId = ((ClaimsIdentity)User.Identity).FindFirst("Id").Value;
   var user = await _userManager.FindByIdAsync(userId);

   ProfileUpdateModel model = new ProfileUpdateModel();
   model.Email = user.Email;
   model.FirstName = user.FirstName;
   model.LastName = user.LastName;
   model.PhoneNumber = user.PhoneNumber;

   return new OkObjectResult(model);
}
Rokive
quelle
0

Ich habe so etwas in meine Controller-Klasse aufgenommen und es hat funktioniert:

IdentityUser user = await userManager.FindByNameAsync(HttpContext.User.Identity.Name);

Dabei ist userManager eine Instanz der Microsoft.AspNetCore.Identity.UserManager-Klasse (mit allen damit verbundenen seltsamen Einstellungen).

Marko
quelle