ASP.NET Core Identity - lấy người dùng hiện tại


82

Để có được người dùng hiện đang đăng nhập trong MVC5, tất cả những gì chúng tôi phải làm là:

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

Bây giờ, với ASP.NET Core, tôi nghĩ rằng điều này sẽ hoạt động, nhưng nó lại xảy ra lỗi.

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

Có ý kiến ​​gì không?

CHỈNH SỬA: Phản hồi của Gerardo đang theo dõi nhưng để có được "Id" thực tế của người dùng, điều này có vẻ hoạt động:

ClaimsPrincipal currentUser = this.User;
var currentUserID = currentUser.FindFirst(ClaimTypes.NameIdentifier).Value;

Câu hỏi của bạn chính xác là gì?
Gerardo Grignoli

Bạn chỉ cần Id? Tôi thay đổi nội dung câu trả lời của tôi để thêm làm thế nào để làm cho nó bằng cách sử dụng nhiều fancy _userManager.GetUserId (Thành viên)
Gerardo Grignoli

Có, chủ yếu tôi cần Id từ bảng AspNetUsers hoặc từ phiên. currentUser.FindFirst (ClaimTypes.NameIdentifier) ​​.Value cung cấp Id bằng cách sử dụng Claims. Nó cũng hoạt động với UserManager! Cảm ơn Gerardo! Một cách có hiệu quả hơn cách kia không?
jbraun

UserManager nội bộ thực hiện .FindFirst (ClaimTypes.NameIdentifier). Vì vậy, nó là cùng một hiệu suất khôn ngoan. Tôi thích cách đóng gói usermanager để dễ đọc hơn. Mặt khác .GetUserAsync()là chậm hơn vì nó đi đến DB.
Gerardo Grignoli

Tôi hoàn toàn đồng ý Gerardo. Cảm ơn bạn !
jbraun

Câu trả lời:


138

Giả sử mã của bạn nằm trong bộ điều khiển MVC:

public class MyController : Microsoft.AspNetCore.Mvc.Controller

Từ Controllerlớp cơ sở, bạn có thể lấy IClaimsPrincipaltừ thuộc Usertính

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

Bạn có thể kiểm tra các yêu cầu trực tiếp (mà không cần đi vòng quanh cơ sở dữ liệu):

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

Các trường khác có thể được tìm nạp từ thực thể Người dùng của cơ sở dữ liệu:

  1. Nhận trình quản lý người dùng bằng cách tiêm phụ thuộc

    private UserManager<ApplicationUser> _userManager;
    
    //class constructor
    public MyController(UserManager<ApplicationUser> userManager)
    {
        _userManager = userManager;
    }
    
  2. Và sử dụng nó:

    var user = await _userManager.GetUserAsync(User);
    var email = user.Email;
    

5
Và nếu tôi đang ở trong một lớp dịch vụ và không ở trong bộ điều khiển, nơi tôi không có Thuộc tính Người dùng? Làm thế nào để lấy AppUser-Object của người dùng đã xác thực?
Kirsten

13
@Kirsten Bạn có thể nhận được IHttpContextAccessorvới Dependency Injection và sau đó nhận Usertừ ngữ cảnh http. Chỉ cần thêm param này để các nhà xây dựng lớp dịch vụ: IHttpContextAccessor contextAccessor. Giữ bộ truy cập trên một trường _contextAccessorvà sau đó trên các phương thức dịch vụ lấy nó từ đó _contextAccessor.HttpContext.User.
Gerardo Grignoli

5
@Kirsten Nhưng việc làm này sẽ kết hợp dịch vụ của bạn với ASP.NET Core, khiến nó không thể sử dụng được trong các loại tình huống khác. Điều này có thể được chấp nhận đối với bạn, hoặc không. Tôi cũng sẽ xem xét việc chuyển chính xác những gì bạn muốn đến dịch vụ trong cuộc gọi phương thức.
Gerardo Grignoli

2
Bạn đã thúc đẩy tôi đúng cách: tôi đã xây dựng một IUserService với GetUser và IsAuthenticated. Trong Ứng dụng lõi ASP.NET của tôi, tôi có một userService triển khai sử dụng IHttpContextAccessor và UserManager <ApplicationUser>. Vì vậy, userService của tôi được kết hợp với ASP.NET Core, các dịch vụ khác của tôi thì không.
Kirsten

2
Không, họ không truy cập vào cơ sở dữ liệu. Kiểm tra nguồn, họ không sử dụng UserStore. github.com/dotnet/corefx/blob/master/src/System.Security.Claims/… github.com/aspnet/Identity/blob/…
Gerardo Grignoli

32

Nếu bạn đang sử dụng Bearing Token Auth, các mẫu trên không trả về Người dùng ứng dụng.

Thay vào đó, hãy sử dụng cái này:

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

Điều này hoạt động trong apsnetcore 2.0. Chưa thử trong các phiên bản trước.


Các mã thông báo của tôi đang được xác thực, nhưng tiền gốc và yêu cầu chưa được đặt. Điều này xảy ra trong DotNet lõi 2.1 và 2.2
ps2goat

@ ps2goat, tôi sẽ tạo một câu hỏi mới để giải quyết vấn đề này và bao gồm mã đang tạo mã thông báo.
Greg Gum

1
Tôi đã hiểu rồi. Chúng tôi không có lược đồ mặc định vì chúng ta có 5 lược đồ khác nhau. Tôi đã phải thêm một phần mềm trung gian để giải mã mã thông báo và gán các xác nhận quyền sở hữu cho người dùng hiện tại. Tôi có lẽ nên viết một câu hỏi và trả lời nó cho những người khác, vì có rất ít nội dung xung quanh tình huống của tôi.
ps2goat

1
ClaimTypes.Name lấy tên người dùng mà tôi đang tìm kiếm thay vì Id. ClaimTypes.NameIdentifier lấy Id của người dùng. Đề phòng trường hợp bất kỳ ai nhìn thấy điều này và tự hỏi làm thế nào để lấy tên người dùng. Cảm ơn câu trả lời của bạn Greg!
Matthew Zourelias

7

Đối với ngữ cảnh, tôi đã tạo một dự án bằng cách sử dụng mẫu Ứng dụng Web ASP.NET Core 2. Sau đó, chọn Ứng dụng web (MVC) rồi nhấn nút Thay đổi xác thực và chọn Tài khoản người dùng cá nhân.

Có rất nhiều cơ sở hạ tầng được xây dựng cho bạn từ mẫu này. Tìm ManageControllertrong thư mục Bộ điều khiển.

Phương thức ManageControllerkhởi tạo lớp này yêu cầu biến UserManager này để phổ biến:

private readonly UserManager<ApplicationUser> _userManager;

Sau đó, hãy xem phương thức Chỉ mục [HttpPost] trong lớp này. Họ có được người dùng hiện tại theo cách này:

var user = await _userManager.GetUserAsync(User);

Lưu ý bổ sung, đây là nơi bạn muốn cập nhật bất kỳ trường tùy chỉnh nào vào Hồ sơ người dùng mà bạn đã thêm vào bảng AspNetUsers. Thêm các trường vào dạng xem, sau đó gửi các giá trị đó đến IndexViewModel, sau đó sẽ được gửi đến phương thức Đăng này. Tôi đã thêm mã này sau logic mặc định để đặt địa chỉ email và số điện thoại:

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

5

Trong .NET Core 2.0, người dùng đã tồn tại như một phần của bộ điều khiển được kế thừa bên dưới. Chỉ cần sử dụng Người dùng như bạn thường làm hoặc chuyển sang bất kỳ mã kho lưu trữ nào.

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

Đây là nơi nó kế thừa nó từ

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

2

Chỉ cần bất kỳ ai quan tâm điều này làm việc cho tôi. Tôi có Identity tùy chỉnh sử dụng int cho khóa chính nên tôi đã ghi đè phương thức GetUserAsync

Ghi đè GetUserAsync

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

Nhận danh tính người dùng

var user = await _userManager.GetUserAsync(User);

Nếu bạn đang sử dụng khóa chính Guid thông thường, bạn không cần ghi đè GetUserAsync. Tất cả điều này là giả định rằng mã thông báo của bạn được định cấu hình chính xác.

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

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

0

Tôi đã đặt một cái gì đó như thế này trong lớp Bộ điều khiển của mình và nó đã hoạt động:

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

trong đó userManager là một phiên bản của lớp Microsoft.AspNetCore.Identity.UserManager (với tất cả các thiết lập kỳ lạ đi kèm với nó).

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.