User.Identityの使用可能なプロパティを拡張する方法


130

ユーザーが私のWebサイトにログインするためにMVC5 Identity 2.0を使用しています。認証の詳細はSQLデータベースに保存されています。Asp.net Identityは、多くのオンラインチュートリアルで見られるように、標準的な方法で実装されています。

IdentityModelsのApplicationUserクラスが拡張され、整数のOrganizationIdなどのカスタムプロパティが含まれるようになりました。多くのユーザーを作成して、データベース関係の目的で共通の組織に割り当てることができます。

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

現在ログインしているユーザーのOrganizationIdプロパティをコントローラー内から取得するにはどうすればよいですか?これは、ユーザーがログインするとメソッドを介して利用できますか、それとも、コントローラーメソッドが実行されるたびに、UserIdに基づいてデータベースから常にOrganizationIdを取得しますか?

私が見たウェブで周りを読んで、ログインしたUserIdなどを取得するために以下を使用する必要があります。

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

ただし、OrganizationIdはUser.Identityで使用できるプロパティではありません。User.Identityを拡張して、OrganizationIdプロパティを含める必要がありますか?もしそうなら、私はこれについてどうやって行くのですか?

私がOrganizationIdを頻繁に必要とする理由は、多くのテーブルクエリが、ログインしたユーザーに関連付けられている組織に関連するデータを取得するためにOrganizationIdに依存しているためです。


3
ここでの私の答えあなたに役立ちますか?

1
ここで私からほとんど同じ答え:stackoverflow.com/a/28138594/809357 -あなたは、要求の生活の中で定期的にこの情報が必要な場合、あなたは主張としてクッキーの上に置くことができます。
Trailmax、2015

1
@Shoeに感謝します。あなたの答えに加えて、私はクッキーに保存されるクレームを追加しなければなりませんでした。IdentityModelsクラスで、userIdentity.AddClaim(new Claim( "MyApp:OrganizationId"、OrganizationId.ToString()));を追加する必要がありましたタスク<ClaimsIdentity> GenerateUserIdentityAsync(のUserManager <ApplicationUser>マネージャ)非同期パブリック 方法。
RobHurd、2015

回答:


219

上記の質問のような追加のプロパティでUser.Identityのプロパティを拡張する場合は常に、最初に次のようにこれらのプロパティをApplicationUserクラスに追加します。

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

次に、必要なのはそのような拡張メソッドを作成することです(私は新しい拡張フォルダに自分を作成します)。

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

ApplicationUserクラスでIdentityを作成するときは、次のようにClaim-> OrganizationIdを追加するだけです。

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

クレームを追加して拡張メソッドを配置したら、User.Identityのプロパティとして使用できるように、アクセスするページ/ファイルにusingステートメントを追加します

私の場合:using App.Extensions;コントローラ内で@using. App.Extensions.cshtmlビューファイルを使用しています。

編集:

すべてのビューにusingステートメントを追加しないようにする方法として、Viewsフォルダーに移動して、そこにあるWeb.configファイルを見つけることもできます。次に、<namespaces>タグを探して、次のように拡張ネームスペースを追加します。

<add namespace="App.Extensions" />

ファイルを保存すれば完了です。これで、すべてのビューが拡張機能を認識します。

拡張メソッドにアクセスできます。

var orgId = User.Identity.GetOrganizationId();

パヴェルこんにちは、私は同じことを試してみましたが、アプリケーションのユーザーはOrganisationIdの定義が含まれていないことを示すエラー取得
それは罠だ

@RachitGuptaそれはいつ起こりますか?クレームを追加しようとしたとき、またはコードで後でその値にアクセスしようとしたとき クレームを追加する場合は、ApplicationUserにプロパティが定義されていることを確認してください...コードの後半にある場合は、次のような拡張メソッドを作成した場所にusingステートメントを追加することを忘れないでください:using App.Extensions ;
Pawel 2016年

userIdentity.AddClaim行にエラーが発生しました。IdentityModels.csファイルのみにIdentityExtensionsクラスを作成しました。それが問題の原因になるのでしょうか?
それはトラップである

いいえ、クレームを追加する場合は、identityExtensionsが機能する前に発生します(つまり、値を読み返すときです)... ApplicationUserに、クレームとして追加しようとしているプロパティがあることを確認します。この例では、 public long OrganizationId {get; セットする; }
Pawel 2016年

6
ありがとう、うまくいきました。これはAsp Net Idendity 2のカスタム変数のベストプラクティスだと思います。Asp.NetコミュニティがWebサイトのデフォルトの記事でそのような例を提供していない理由がわかりません。
oneNic​​eFriend

17

私は同じ解決策を探していて、Pawelが答えの99%をくれました。拡張機能を表示するために必要な唯一の欠けていたのは、cshtml(view)ページに次のRazorコードを追加することでした。

@using programname.Models.Extensions

ユーザーがログインした後、NavBarの右上に表示するFirstNameを探していました。

私はこれが誰かを助ける場合に備えてこれを投稿すると思ったので、ここに私のコードがあります:

Extensions(Under my Models Folder)という新しいフォルダーを作成し、上記で指定したPawelとして新しいクラスを作成しました。 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;
    }
}

次に、_LoginPartial.cshtmlViews/Sharedフォルダの下)に追加しました@using.ProgramName.Models.Extensions

次に、ログイン後にユーザー名を使用するコードの次の行に変更を追加しました。

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

おそらく、これは将来の誰かを助けるでしょう。


11

John Attenによるこの素晴らしいブログ投稿をチェックしてください: ASP.NET Identity 2.0:ユーザーとロールのカスタマイズ

プロセス全体のステップバイステップの優れた情報があります。読んでください:)

ここにいくつかの基本があります。

新しいプロパティ(つまり、住所、市、州など)を追加して、デフォルトのApplicationUserクラスを拡張します。

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

次に、RegisterViewModelに新しいプロパティを追加します。

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

次に、Register Viewを更新して、新しいプロパティを含めます。

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

次に、新しいプロパティでAccountControllerのRegister()メソッドを更新します。

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

16
これは良い例ですが、User.Identityからこれらの新しいプロパティをどのように取得するかという質問には答えません。
Dejan Bogatinovski

5
回答にはカスタムプロパティをUser.Identityから取得する方法が示されていないため、反対票が投じられました。
maulik13 2016

3

ASP.NET Core 2.1でカスタムプロパティにアクセスする方法を探してこの質問を見つけた人は誰でも-はるかに簡単です。たとえば、_LoginPartial.cshtmlにUserManagerがあれば、簡単に実行できます( "ScreenName"がIdentityUserから継承する独自のAppUserに追加したプロパティ):

@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
GetUserAsync(User)は、データベースを照会してOrganizationIdを取得することに注意してください。対照的に、受け入れられたソリューションでは、クレーム(Cookieなど)にOrganizationIdが含まれます。この情報をデータベースから取得することの利点は、ユーザーがログアウト/ログインすることなく組織間を移動できることです。もちろん、欠点は、追加のデータベースクエリが必要になることです。
Matt

1

Dhaustを使用すると、ApplicationUserクラスにプロパティを追加できます。OPコードを見ると、彼らはこれを行ったか、それを行うために軌道に乗っていたようです。質問は尋ねます

現在ログインしているユーザーのOrganizationIdプロパティをコントローラー内から取得するにはどうすればよいですか?ただし、OrganizationIdはUser.Identityで使用できるプロパティではありません。User.Identityを拡張して、OrganizationIdプロパティを含める必要がありますか?

Pawelは、ステートメントの使用または名前空間のweb.configファイルへの追加を必要とする拡張メソッドを追加する方法を提供します。

ただし、この質問では、User.Identityを拡張して新しいプロパティを含める必要があるかどうかを尋ねられます。User.Identityを拡張せずにプロパティにアクセスする別の方法があります。Dhaustメソッドを使用した場合は、コントローラーで次のコードを使用して新しいプロパティにアクセスできます。

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

また、AspNetUsersテーブルに列を追加または拡張しました。このデータを単純に表示したいとき、「Extensions」などを含む上記のコードのような多くの例を見つけました...これは、現在のユーザーからいくつかの値を取得するためだけにこれらのコード行をすべて記述しなければならなかったことに本当に驚いています。

AspNetUsersテーブルを他のテーブルと同様にクエリできることがわかります。

 ApplicationDbContext db = new ApplicationDbContext();
 var user = db.Users.Where(x => x.UserName == User.Identity.Name).FirstOrDefault();
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.