1. ホーム
  2. asp.net

[解決済み] ASP.NET MVC - カスタムIIdentityまたはIPrincipalの設定

2022-03-15 21:17:15

質問

ASP.NET MVCアプリケーションで、カスタムのIIdentity/IPrincipalを設定したいのですが、簡単なことでしょうか?どちらか簡単な方、またはより適切な方。私は、デフォルトを拡張して、次のようなものを呼び出すことができるようにしたい。 User.Identity.IdUser.Identity.Role . 何も派手なことはせず、いくつかのプロパティを追加しただけです。

大量の記事や質問を読みましたが、実際より難しくしているような気がします。私はそれが簡単だと思った。ユーザーがログオンした場合、私はカスタムIIdentityを設定したいと思います。だから、私は実装しようと思いました Application_PostAuthenticateRequest をglobal.asaxに追加しました。しかし、それはすべてのリクエストで呼び出されます。そして、私はすべてのリクエストでデータベースへの呼び出しを行い、データベースからすべてのデータを要求し、カスタムIPrincipalオブジェクトを置くことはしたくありません。それはまた、非常に不必要で、遅く、間違った場所(そこでデータベースを呼び出すこと)にあるように思えますが、私は間違っているかもしれません。また、そのデータはどこから来るのでしょうか?

そこで私は、ユーザーがログインするたびに、必要な変数をセッションに追加し、その変数を Application_PostAuthenticateRequest イベントハンドラです。しかし、私の Context.Sessionnull があるので、それもNGです。

1日前から作業しているのですが、何か足りないような気がします。これって、そんなに難しくないはずですよね?また、これに付随する(半)関連事項にも少し戸惑っています。 MembershipProvider , MembershipUser , RoleProvider , ProfileProvider , IPrincipal , IIdentity , FormsAuthentication .... このようなことを考えると、とても混乱するのは私だけでしょうか?

IIdentityに余分なデータを保存するための、シンプルでエレガントで効率的な解決策を教えてもらえたら、とても嬉しいです。SOに似たような質問があるのは知っていますが、もし私が必要とする答えがそこにあるのなら、私は見落としていたに違いありません。

どのように解決するのですか?

以下はその方法です。

IIdentityの代わりにIPrincipalを使うことにしたのは、IIdentityとIPrincipalの両方を実装する必要がないためだ。

  1. インターフェイスの作成

    interface ICustomPrincipal : IPrincipal
    {
        int Id { get; set; }
        string FirstName { get; set; }
        string LastName { get; set; }
    }
    
    
  2. CustomPrincipal

    public class CustomPrincipal : ICustomPrincipal
    {
        public IIdentity Identity { get; private set; }
        public bool IsInRole(string role) { return false; }
    
        public CustomPrincipal(string email)
        {
            this.Identity = new GenericIdentity(email);
        }
    
        public int Id { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
    }
    
    
  3. CustomPrincipalSerializeModel - FormsAuthenticationTicketオブジェクトのuserdataフィールドにカスタム情報をシリアライズするためのモデルです。

    public class CustomPrincipalSerializeModel
    {
        public int Id { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
    }
    
    
  4. ログイン方法 - カスタム情報を含むクッキーの設定

    if (Membership.ValidateUser(viewModel.Email, viewModel.Password))
    {
        var user = userRepository.Users.Where(u => u.Email == viewModel.Email).First();
    
        CustomPrincipalSerializeModel serializeModel = new CustomPrincipalSerializeModel();
        serializeModel.Id = user.Id;
        serializeModel.FirstName = user.FirstName;
        serializeModel.LastName = user.LastName;
    
        JavaScriptSerializer serializer = new JavaScriptSerializer();
    
        string userData = serializer.Serialize(serializeModel);
    
        FormsAuthenticationTicket authTicket = new FormsAuthenticationTicket(
                 1,
                 viewModel.Email,
                 DateTime.Now,
                 DateTime.Now.AddMinutes(15),
                 false,
                 userData);
    
        string encTicket = FormsAuthentication.Encrypt(authTicket);
        HttpCookie faCookie = new HttpCookie(FormsAuthentication.FormsCookieName, encTicket);
        Response.Cookies.Add(faCookie);
    
        return RedirectToAction("Index", "Home");
    }
    
    
  5. Global.asax.cs - クッキーを読み込んで、HttpContext.Userオブジェクトを置き換える、これはPostAuthenticateRequestをオーバーライドすることによって行われる

    protected void Application_PostAuthenticateRequest(Object sender, EventArgs e)
    {
        HttpCookie authCookie = Request.Cookies[FormsAuthentication.FormsCookieName];
    
        if (authCookie != null)
        {
            FormsAuthenticationTicket authTicket = FormsAuthentication.Decrypt(authCookie.Value);
    
            JavaScriptSerializer serializer = new JavaScriptSerializer();
    
            CustomPrincipalSerializeModel serializeModel = serializer.Deserialize<CustomPrincipalSerializeModel>(authTicket.UserData);
    
            CustomPrincipal newUser = new CustomPrincipal(authTicket.Name);
            newUser.Id = serializeModel.Id;
            newUser.FirstName = serializeModel.FirstName;
            newUser.LastName = serializeModel.LastName;
    
            HttpContext.Current.User = newUser;
        }
    }
    
    
  6. Razorのビューでのアクセス

    @((User as CustomPrincipal).Id)
    @((User as CustomPrincipal).FirstName)
    @((User as CustomPrincipal).LastName)
    
    

とコードで表示します。

    (User as CustomPrincipal).Id
    (User as CustomPrincipal).FirstName
    (User as CustomPrincipal).LastName

このコードは自明だと思います。そうでない場合は、教えてください。

さらに、アクセスをより簡単にするために、ベースコントローラを作成し、返されたUserオブジェクト(HttpContext.User)をオーバーライドすることができます。

public class BaseController : Controller
{
    protected virtual new CustomPrincipal User
    {
        get { return HttpContext.User as CustomPrincipal; }
    }
}

とし、各コントローラーごとに

public class AccountController : BaseController
{
    // ...
}

を使用すると、このようなコードでカスタムフィールドにアクセスできるようになります。

User.Id
User.FirstName
User.LastName

しかし、これはビューの中では動作しません。そのためには、カスタムのWebViewPageの実装を作成する必要があります。

public abstract class BaseViewPage : WebViewPage
{
    public virtual new CustomPrincipal User
    {
        get { return base.User as CustomPrincipal; }
    }
}

public abstract class BaseViewPage<TModel> : WebViewPage<TModel>
{
    public virtual new CustomPrincipal User
    {
        get { return base.User as CustomPrincipal; }
    }
}

Views/web.configでデフォルトのページタイプにする。

<pages pageBaseType="Your.Namespace.BaseViewPage">
  <namespaces>
    <add namespace="System.Web.Mvc" />
    <add namespace="System.Web.Mvc.Ajax" />
    <add namespace="System.Web.Mvc.Html" />
    <add namespace="System.Web.Routing" />
  </namespaces>
</pages>

で、viewsでは、このようにアクセスします。

@User.FirstName
@User.LastName