Cách mở rộng các thuộc tính có sẵn của User.Identity


130

Tôi đang sử dụng MVC5 Identity 2.0 để người dùng đăng nhập vào trang web của tôi, nơi các chi tiết xác thực được lưu trữ trong cơ sở dữ liệu SQL. Danh tính Asp.net đã được triển khai theo cách tiêu chuẩn như có thể tìm thấy trong nhiều hướng dẫn trực tuyến.

Lớp ApplicationUser trong IdentityModels đã được mở rộng để bao gồm một số thuộc tính tùy chỉnh, chẳng hạn như một tổ chức số nguyên. Ý tưởng là nhiều người dùng có thể được tạo và gán cho một Tổ chức chung cho các mục đích quan hệ cơ sở dữ liệu.

public class ApplicationUser : IdentityUser
    {
        public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<ApplicationUser> manager)
        {
            // Note the authenticationType must match the one defined in CookieAuthenticationOptions.AuthenticationType
            var userIdentity = await manager.CreateIdentityAsync(this, DefaultAuthenticationTypes.ApplicationCookie);
            // Add custom user claims here
            return userIdentity;
        }

        //Extended Properties
        public DateTime? BirthDate { get; set; }
        public long? OrganizationId { get; set; }

        //Key Mappings
        [ForeignKey("OrganizationId")]
        public virtual Organization Organization { get; set; }
    }

Làm cách nào tôi có thể truy xuất thuộc tính Organisd của người dùng hiện đang đăng nhập từ bên trong bộ điều khiển? Điều này có sẵn thông qua một phương thức một khi người dùng đã đăng nhập hay tôi luôn luôn lấy Tổ chức từ cơ sở dữ liệu, dựa trên UserId, mỗi khi phương thức bộ điều khiển thực thi?

Đọc trên web tôi đã thấy tôi cần sử dụng những điều sau đây để đăng nhập UserId, v.v.

using Microsoft.AspNet.Identity;
...
User.Identity.GetUserId();

Tuy nhiên, Organisd không phải là một tài sản có sẵn trong User.Identity. Tôi có cần gia hạn User.Identity để bao gồm thuộc tính Organisd không? Nếu vậy, làm thế nào để tôi đi về điều này.

Lý do tôi cần Tổ chức thường xuyên là vì nhiều truy vấn bảng phụ thuộc vào Tổ chức để truy xuất dữ liệu liên quan đến Tổ chức có liên quan đến người dùng đã đăng nhập.


3
Câu trả lời của tôi ở đây có giúp gì cho bạn không?
Giày

1
Khá nhiều câu trả lời tương tự từ tôi ở đây: stackoverflow.com/a/28138594/809357 - nếu bạn cần thông tin này thường xuyên trong vòng đời của yêu cầu, bạn có thể đặt nó trên cookie như một yêu cầu.
trailmax

1
Cảm ơn @Shoe cả hai câu trả lời của bạn đã làm việc. Ngoài câu trả lời của bạn, tôi đã phải thêm một yêu cầu được lưu trữ trong cookie. Trong lớp IdentityModels, tôi đã phải thêm userIdentity.AddClaim (Yêu cầu mới ("MyApp: Organisd", Organisd.ToString ())); đến phương thức Nhiệm vụ async công khai <ClaimsIdentity> GenerateUserIdentityAsync (UserManager <ApplicationUser>) .
RobHurd 8/2/2015

Câu trả lời:


219

Bất cứ khi nào bạn muốn mở rộng các thuộc tính của User.Identity với bất kỳ thuộc tính bổ sung nào như câu hỏi ở trên, trước tiên hãy thêm các thuộc tính này vào lớp ApplicationUser như sau:

public class ApplicationUser : IdentityUser
{
    public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<ApplicationUser> manager)
    {
        // Note the authenticationType must match the one defined in CookieAuthenticationOptions.AuthenticationType
        var userIdentity = await manager.CreateIdentityAsync(this, DefaultAuthenticationTypes.ApplicationCookie);
        // Add custom user claims here
        return userIdentity;
    }

    // Your Extended Properties
    public long? OrganizationId { get; set; }
}

Sau đó, những gì bạn cần là tạo một phương thức mở rộng như vậy (Tôi tạo của tôi trong một thư mục Tiện ích mở rộng mới):

namespace App.Extensions
{
    public static class IdentityExtensions
    {
        public static string GetOrganizationId(this IIdentity identity)
        {
            var claim = ((ClaimsIdentity)identity).FindFirst("OrganizationId");
            // Test for null to avoid issues during local testing
            return (claim != null) ? claim.Value : string.Empty;
        }
    }
}

Khi bạn tạo Danh tính trong lớp ApplicationUser, chỉ cần thêm Yêu cầu -> Tổ chức như vậy:

    public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<ApplicationUser> manager)
    {
        // Note the authenticationType must match the one defined in CookieAuthenticationOptions.AuthenticationType
        var userIdentity = await manager.CreateIdentityAsync(this, DefaultAuthenticationTypes.ApplicationCookie);
        // Add custom user claims here => this.OrganizationId is a value stored in database against the user
        userIdentity.AddClaim(new Claim("OrganizationId", this.OrganizationId.ToString()));

        return userIdentity;
    }

Khi bạn đã thêm khiếu nại và áp dụng phương thức tiện ích mở rộng của mình, để cung cấp dưới dạng tài sản trên User.Identity, hãy thêm một tuyên bố sử dụng trên trang / tệp bạn muốn truy cập :

trong trường hợp của tôi: using App.Extensions;trong Bộ điều khiển và @using. App.Extensionsbỏ tập tin Xem .cshtml.

BIÊN TẬP:

Điều bạn cũng có thể làm để tránh thêm câu lệnh sử dụng trong mỗi Chế độ xem là vào thư mục Lượt xem và tìm tệp Web.config trong đó. Bây giờ hãy tìm <namespaces>thẻ và thêm không gian tên mở rộng của bạn vào đó như sau:

<add namespace="App.Extensions" />

Lưu tập tin của bạn và bạn đã hoàn tất. Bây giờ mọi Chế độ xem sẽ biết về tiện ích mở rộng của bạn.

Bạn có thể truy cập Phương thức mở rộng:

var orgId = User.Identity.GetOrganizationId();

Xin chào pawel, tôi đã thử tương tự, nhưng gặp lỗi khi người dùng Ứng dụng không chứa định nghĩa của OrganisationId
Đó là một cái bẫy

@RachitGupta Khi nào nó xảy ra? Khi bạn cố gắng thêm yêu cầu hoặc khi bạn cố gắng truy cập giá trị của nó sau này trong mã? Nếu đó là khi bạn thêm khiếu nại thì hãy đảm bảo ApplicationUser của bạn có thuộc tính được xác định ... Nếu sau này trong mã thì đừng quên thêm câu lệnh sử dụng vào nơi bạn đã tạo phương thức tiện ích mở rộng như: sử dụng App.Extensions ;
Pawel

Có lỗi trong dòng userIdentity.AddClaim. Tôi đã tạo lớp IdentityExtensions chỉ trong tệp IdentityModels.cs. Đó có thể là một nguồn gốc của vấn đề?
Đó là một cái bẫy

Không, nếu đó là khi bạn thêm khiếu nại thì điều đó xảy ra trước khi nhận dạng Mở rộng (đó là khi bạn đọc lại giá trị) ... Hãy chắc chắn rằng Người sử dụng ứng dụng của bạn có tài sản bạn đang cố gắng thêm vào như yêu cầu: trong ví dụ này là công khai tổ chức dài {nhận; bộ; }
Pawel

6
Cảm ơn, nó đã làm việc. Tôi nghĩ rằng đây là cách thực hành tốt nhất cho các biến tùy chỉnh trong Asp Net Idendity 2. Tôi không biết tại sao cộng đồng Asp.Net không cung cấp một ví dụ như vậy trong các bài viết mặc định trên trang web của họ.
thân vào

17

Tôi đang tìm kiếm giải pháp tương tự và Pawel đã cho tôi 99% câu trả lời. Điều duy nhất còn thiếu mà tôi cần cho Tiện ích mở rộng để hiển thị là thêm Mã Dao cạo sau vào trang cshtml (chế độ xem):

@using programname.Models.Extensions

Tôi đang tìm FirstName, để hiển thị ở phía trên bên phải NavBar của tôi sau khi người dùng đăng nhập.

Tôi nghĩ rằng tôi sẽ đăng bài này trong trường hợp nó giúp người khác, vì vậy đây là mã của tôi:

Tôi đã tạo một thư mục mới có tên là Tiện ích mở rộng (Trong Thư mục mô hình của tôi) và tạo lớp mới như Pawel đã chỉ định ở trên: IdentityExtensions.cs

using System.Security.Claims;
using System.Security.Principal;

namespace ProgramName.Models.Extensions
{
    public static class IdentityExtensions
    {
        public static string GetUserFirstname(this IIdentity identity)
        {
            var claim = ((ClaimsIdentity)identity).FindFirst("FirstName");
            // Test for null to avoid issues during local testing
            return (claim != null) ? claim.Value : string.Empty;
        }
    }
}

IdentityModels.cs :

public class ApplicationUser : IdentityUser
{

    //Extended Properties
    public string FirstName { get; internal set; }
    public string Surname { get; internal set; }
    public bool isAuthorized { get; set; }
    public bool isActive { get; set; }

    public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<ApplicationUser> manager)
    {
        // Note the authenticationType must match the one defined in CookieAuthenticationOptions.AuthenticationType
        var userIdentity = await manager.CreateIdentityAsync(this, DefaultAuthenticationTypes.ApplicationCookie);
        // Add custom user claims here
        userIdentity.AddClaim(new Claim("FirstName", this.FirstName));

        return userIdentity;
    }
}

Sau đó, trong _LoginPartial.cshtml(Dưới Views/Sharedthư mục) tôi đã thêm@using.ProgramName.Models.Extensions

Sau đó, tôi đã thêm thay đổi vào dòng mã theo dõi sẽ sử dụng Tên người dùng sau khi đăng nhập:

@Html.ActionLink("Hello " + User.Identity.GetUserFirstname() + "!", "Index", "Manage", routeValues: null, htmlAttributes: new { title = "Manage" })

Có lẽ điều này giúp người khác xuống dòng.


11

Kiểm tra bài đăng blog tuyệt vời này của John Atten: ASP.NET Identity 2.0: Tùy chỉnh người dùng và vai trò

Nó có thông tin từng bước tuyệt vời trên toàn bộ quá trình. Đi đọc nó :)

Dưới đây là một số điều cơ bản.

Mở rộng lớp ApplicationUser mặc định bằng cách thêm các thuộc tính mới (ví dụ: Địa chỉ, Thành phố, Bang, v.v.):

public class ApplicationUser : IdentityUser
{
    public async Task<ClaimsIdentity> 
    GenerateUserIdentityAsync(UserManager<ApplicationUser> manager)
    {
        var userIdentity = await manager.CreateIdentityAsync(this,  DefaultAuthenticationTypes.ApplicationCookie);
        return userIdentity;
    }
    public string Address { get; set; }
    public string City { get; set; }
    public string State { get; set; }

    // Use a sensible display name for views:
    [Display(Name = "Postal Code")]
    public string PostalCode { get; set; }

    // Concatenate the address info for display in tables and such:
    public string DisplayAddress
    {
        get
        {
            string dspAddress = string.IsNullOrWhiteSpace(this.Address) ? "" : this.Address;
            string dspCity = string.IsNullOrWhiteSpace(this.City) ? "" : this.City;
            string dspState = string.IsNullOrWhiteSpace(this.State) ? "" : this.State;
            string dspPostalCode = string.IsNullOrWhiteSpace(this.PostalCode) ? "" : this.PostalCode;

            return string.Format("{0} {1} {2} {3}", dspAddress, dspCity, dspState, dspPostalCode);
        }
    }

Sau đó, bạn thêm các thuộc tính mới của mình vào RegisterViewModel.

    // Add the new address properties:
    public string Address { get; set; }
    public string City { get; set; }
    public string State { get; set; }

Sau đó cập nhật Chế độ xem Đăng ký để bao gồm các thuộc tính mới.

    <div class="form-group">
        @Html.LabelFor(m => m.Address, new { @class = "col-md-2 control-label" })
        <div class="col-md-10">
            @Html.TextBoxFor(m => m.Address, new { @class = "form-control" })
        </div>
    </div>

Sau đó cập nhật phương thức Register () trên AccountControll với các thuộc tính mới.

    // Add the Address properties:
    user.Address = model.Address;
    user.City = model.City;
    user.State = model.State;
    user.PostalCode = model.PostalCode;

16
Đây là một ví dụ tốt nhưng nó không trả lời câu hỏi đó là làm thế nào để bạn có được những thuộc tính mới đó từ User.Identity.
Dejan Bogatinovski

5
Bị từ chối vì câu trả lời không hiển thị cách các thuộc tính tùy chỉnh có thể được truy xuất từ ​​User.Identity.
maulik13

3

Đối với bất kỳ ai tìm thấy câu hỏi này đang tìm cách truy cập các thuộc tính tùy chỉnh trong ASP.NET Core 2.1 - sẽ dễ dàng hơn nhiều: Bạn sẽ có UserManager, ví dụ như trong _LoginPartial.cshtml, sau đó bạn có thể chỉ cần làm (giả sử "ScreenName" là một thuộc tính mà bạn đã thêm vào AppUser của riêng bạn kế thừa từ IdentityUser):

@using Microsoft.AspNetCore.Identity

@using <namespaceWhereYouHaveYourAppUser>

@inject SignInManager<AppUser> SignInManager
@inject UserManager<AppUser> UserManager

@if (SignInManager.IsSignedIn(User)) {
    <form asp-area="Identity" asp-page="/Account/Logout" asp-route-returnUrl="@Url.Action("Index", "Home", new { area = "" })" 
          method="post" id="logoutForm" 
          class="form-inline my-2 my-lg-0">

        <ul class="nav navbar-nav ml-auto">
            <li class="nav-item">
                <a class="nav-link" asp-area="Identity" asp-page="/Account/Manage/Index" title="Manage">
                    Hello @((await UserManager.GetUserAsync(User)).ScreenName)!
                    <!-- Original code, shows Email-Address: @UserManager.GetUserName(User)! -->
                </a>
            </li>
            <li class="nav-item">
                <button type="submit" class="btn btn-link nav-item navbar-link nav-link">Logout</button>
            </li>
        </ul>

    </form>
} else {
    <ul class="navbar-nav ml-auto">
        <li class="nav-item"><a class="nav-link" asp-area="Identity" asp-page="/Account/Register">Register</a></li>
        <li class="nav-item"><a class="nav-link" asp-area="Identity" asp-page="/Account/Login">Login</a></li>
    </ul>
}

2
Cần lưu ý rằng GetUserAsync(User)sẽ truy vấn cơ sở dữ liệu để lấy Tổ chức. Ngược lại, giải pháp được chấp nhận sẽ bao gồm Organisd trong các khiếu nại (ví dụ: cookie). Ưu điểm của việc lấy thông tin này từ cơ sở dữ liệu là mọi người có thể được di chuyển giữa các tổ chức mà không yêu cầu họ đăng xuất / đăng nhập. Tất nhiên, nhược điểm là nó yêu cầu một truy vấn cơ sở dữ liệu bổ sung.
Matt

1

DTHER cung cấp một cách tốt để thêm thuộc tính vào lớp ApplicationUser. Nhìn vào mã OP có vẻ như họ có thể đã làm điều này hoặc đang đi đúng hướng để làm điều đó. Câu hỏi

Làm cách nào tôi có thể truy xuất thuộc tính Organisd của người dùng hiện đang đăng nhập từ bên trong bộ điều khiển? Tuy nhiên, Organisd không phải là một tài sản có sẵn trong User.Identity. Tôi có cần gia hạn User.Identity để bao gồm thuộc tính Organisd không?

Pawel đưa ra một cách để thêm một phương thức mở rộng yêu cầu sử dụng các câu lệnh hoặc thêm không gian tên vào tệp web.config.

Tuy nhiên, câu hỏi hỏi bạn có "cần" mở rộng User.Identity để bao gồm tài sản mới không. Có một cách khác để truy cập vào tài sản mà không cần mở rộng User.Identity. Nếu bạn đã làm theo phương pháp DTHER, thì bạn có thể sử dụng đoạn mã sau trong bộ điều khiển của mình để truy cập thuộc tính mới.

ApplicationDbContext db = new ApplicationDbContext();
var manager = new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(db));
var currentUser = manager.FindById(User.Identity.GetUserId());
var myNewProperty = currentUser.OrganizationId;

0

Tôi cũng đã thêm hoặc mở rộng các cột bổ sung vào bảng AspNetUsers của mình. Khi tôi chỉ muốn xem dữ liệu này, tôi đã tìm thấy nhiều ví dụ như mã ở trên với "Tiện ích mở rộng", v.v ... Điều này thực sự làm tôi ngạc nhiên rằng bạn phải viết tất cả các dòng mã đó chỉ để có được một vài giá trị từ người dùng hiện tại.

Hóa ra bạn có thể truy vấn bảng AspNetUsers như bất kỳ bảng nào khác:

 ApplicationDbContext db = new ApplicationDbContext();
 var user = db.Users.Where(x => x.UserName == User.Identity.Name).FirstOrDefault();
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.