ASP.NET MVC-تعيين IIdentity المخصصة أو IPrincipal




asp.net-mvc forms-authentication (6)

أحتاج إلى القيام بشيء بسيط إلى حد ما: في تطبيق ASP.NET MVC ، أريد تعيين IIdentity / IPrincipal مخصص. أيهما أسهل / أكثر ملاءمة. أرغب في تمديد User.Identity.Id الافتراضي بحيث يمكنني الاتصال بشيء مثل User.Identity.Id و User.Identity.Role . لا شيء يتوهم ، فقط بعض خصائص إضافية.

لقد قرأت الكثير من المقالات والأسئلة ولكنني أشعر بأنني أصعب الأمر أكثر مما هو عليه في الواقع. كنت اعتقد انه سيكون سهل. إذا قام مستخدم بتسجيل الدخول ، أريد تعيين IIdentity مخصصة. لذا فكرت ، سأقوم بتنفيذ Application_PostAuthenticateRequest في my.Inx. ومع ذلك ، يتم استدعاؤه على كل طلب ، ولا أريد إجراء مكالمة إلى قاعدة البيانات في كل طلب يطلب كل البيانات من قاعدة البيانات ووضعها في كائن مخصص IPrincipal. ويبدو ذلك أيضًا غير ضروري ، وبطيئًا ، وفي المكان الخطأ (إجراء مكالمات قاعدة البيانات هناك) ولكنني قد أكون مخطئًا. أو من أين تأتي تلك البيانات؟

لذا فكرت ، كلما قام مستخدم بتسجيل الدخول ، يمكنني إضافة بعض المتغيرات الضرورية في جلسة العمل الخاصة بي ، والتي أضفتها إلى IIdentity المخصصة في معالج الأحداث Application_PostAuthenticateRequest . ومع ذلك ، فإن Context.Session بي هو null هناك ، لذلك ليس هذا هو الطريق للذهاب.

لقد كنت أعمل على هذا منذ يوم واحد وأشعر أنني في عداد المفقودين. هذا لا ينبغي أن يكون من الصعب جدا القيام به ، أليس كذلك؟ أنا أيضا مرتبك قليلا من قبل جميع الاشياء (شبه) ذات الصلة التي تأتي مع هذا. MembershipProvider ، MembershipUser ، RoleProvider ، RoleProvider ، RoleProvider ، IIdentity ، FormsAuthentication .... هل أنا الوحيد الذي يجد كل هذا مربكًا للغاية؟

إذا كان بإمكان أحدهم أن يخبرني عن حل بسيط وأنيق وفعال لتخزين بعض البيانات الإضافية على موقع IIdentity دون كل ضبابية إضافية .. فسيكون هذا أمرًا رائعًا! أعلم أن هناك أسئلة مماثلة حول SO لكن إذا كان الجواب الذي أحتاجه موجودًا ، يجب أن أغفل.


استناداً إلى إجابة LukeP ، وإضافة بعض الأساليب لإعداد timeout requireSSL تعاونت مع Web.config .

الروابط المراجع

رموز معدلة من LukeP

1 ، قم بتعيين timeout استناداً إلى Web.Config . ستحصل FormsAuthentication.Timeout على قيمة المهلة المحددة في web.config. أنا ملفوفة ما يلي لتكون وظيفة ، والتي تعود ticket العودة.

int version = 1;
DateTime now = DateTime.Now;

// respect to the `timeout` in Web.config.
TimeSpan timeout = FormsAuthentication.Timeout;
DateTime expire = now.Add(timeout);
bool isPersist = false;

FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(
     version,          
     name,
     now,
     expire,
     isPersist,
     userData);

2 ، قم بتكوين ملف تعريف الارتباط لتكون آمنة أم لا ، على أساس تكوين RequireSSL .

HttpCookie faCookie = new HttpCookie(FormsAuthentication.FormsCookieName, encTicket);
// respect to `RequreSSL` in `Web.Config`
bool bSSL = FormsAuthentication.RequireSSL;
faCookie.Secure = bSSL;

حسنًا ، فأنا شخصًا خفيًا جادًا هنا عن طريق سحب هذا السؤال القديم جدًا ، ولكن هناك نهجًا أكثر بساطة لهذا ، والذي تم التطرق إليه من قبلBaserz أعلاه. وهذا هو استخدام مزيج من أساليب تمديد C # والتخزين المؤقت (لا تستخدم الجلسة).

في الواقع ، قام Microsoft بالفعل بتوفير عدد من هذه الملحقات في مساحة الاسم Microsoft.AspNet.Identity.IdentityExtensions . على سبيل المثال ، GetUserId() هي طريقة ملحق تقوم بإرجاع معرف المستخدم. هناك أيضا GetUserName() و FindFirstValue() ، والتي ترجع المطالبات القائمة على IPrincipal.

لذلك تحتاج فقط إلى تضمين مساحة الاسم ، ثم استدعاء User.Identity.GetUserName() للحصول على اسم المستخدمين كما تم تكوينه بواسطة ASP.NET Identity.

لست متأكداً إذا كان هذا مخزناً مؤقتاً ، حيث أن هوية ASP.NET القديمة ليست مفتوحة المصدر ، ولم أكن قد أزعجت هندسة عكسها. ومع ذلك ، إذا لم يكن الأمر كذلك ، فيمكنك كتابة طريقة الإضافة الخاصة بك ، والتي ستخزن هذه النتيجة مؤقتًا لفترة محددة من الوقت.


لا أستطيع التحدث مباشرة عن ASP.NET MVC ، ولكن بالنسبة لنماذج ASP.NET على الويب ، فإن الخدعة هي إنشاء FormsAuthenticationTicket وتشفيرها في ملف تعريف الارتباط بمجرد مصادقة المستخدم. بهذه الطريقة ، عليك فقط استدعاء قاعدة البيانات مرة واحدة (أو AD أو أي شيء تستخدمه لإجراء المصادقة الخاصة بك) ، وسيتم مصادقة كل طلب لاحق بناءً على التذكرة المخزنة في ملف تعريف الارتباط.

مقالة جيدة حول هذا: http://www.ondotnet.com/pub/a/dotnet/2004/02/02/effectiveformsauth.html (رابط معطل)

تصحيح:

بما أن الرابط أعلاه مكسور ، أود أن أوصي بحل LukeP في إجابته أعلاه: share - أود أيضًا أن أقترح تغيير الإجابة المقبولة على ذلك.

تعديل 2: بديل للارتباط المعطل: https://web.archive.org/web/20120422011422/http://ondotnet.com/pub/a/dotnet/2004/02/02/effectiveformsauth.html


هنا مثال لإنجاز المهمة. يتم تعيين bool isValid من خلال النظر إلى بعض مخازن البيانات (دعنا نقول قاعدة بيانات المستخدم الخاصة بك). معرف المستخدم هو مجرد معرف أحتفظ به. يمكنك إضافة معلومات aditional مثل عنوان البريد الإلكتروني لبيانات المستخدم.

protected void btnLogin_Click(object sender, EventArgs e)
{         
    //Hard Coded for the moment
    bool isValid=true;
    if (isValid) 
    {
         string userData = String.Empty;
         userData = userData + "UserID=" + userID;
         FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(1, username, DateTime.Now, DateTime.Now.AddMinutes(30), true, userData);
         string encTicket = FormsAuthentication.Encrypt(ticket);
         HttpCookie faCookie = new HttpCookie(FormsAuthentication.FormsCookieName, encTicket);
         Response.Cookies.Add(faCookie);
         //And send the user where they were heading
         string redirectUrl = FormsAuthentication.GetRedirectUrl(username, false);
         Response.Redirect(redirectUrl);
     }
}

في asax golbal قم بإضافة التعليمة البرمجية التالية إلى retrive معلوماتك

protected void Application_AuthenticateRequest(Object sender, EventArgs e)
{
    HttpCookie authCookie = Request.Cookies[
             FormsAuthentication.FormsCookieName];
    if(authCookie != null)
    {
        //Extract the forms authentication cookie
        FormsAuthenticationTicket authTicket = 
               FormsAuthentication.Decrypt(authCookie.Value);
        // Create an Identity object
        //CustomIdentity implements System.Web.Security.IIdentity
        CustomIdentity id = GetUserIdentity(authTicket.Name);
        //CustomPrincipal implements System.Web.Security.IPrincipal
        CustomPrincipal newUser = new CustomPrincipal();
        Context.User = newUser;
    }
}

عندما تستخدم المعلومات لاحقًا ، يمكنك الوصول إلى مدير البريد المخصص الخاص بك كما يلي.

(CustomPrincipal)this.User
or 
(CustomPrincipal)this.Context.User

سيسمح لك ذلك بالوصول إلى معلومات المستخدم المخصصة.


وإليك كيف أفعل ذلك.

قررت استخدام IPrincipal بدلا من IIdentity لأنه يعني أنني لست بحاجة إلى تنفيذ كل من 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 - من أجل تسلسل المعلومات المخصصة إلى حقل userdata في كائن FormsAuthenticationTicket.

    public class CustomPrincipalSerializeModel
    {
        public int Id { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
    }
    
  4. طريقة LogIn - إعداد ملف تعريف ارتباط مع معلومات مخصصة

    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

أعتقد أن الشفرة لا تحتاج إلى شرح. إذا لم يكن كذلك ، أخبرني.

بالإضافة إلى ذلك ، لجعل الوصول أكثر سهولة ، يمكنك إنشاء وحدة تحكم أساسية وتجاوز كائن المستخدم الذي تم إرجاعه (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; }
    }
}

جعله نوع صفحة افتراضي في طرق العرض / 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>

وفي طرق العرض ، يمكنك الوصول إليه على النحو التالي:

@User.FirstName
@User.LastName

يوفر لك MVC طريقة OnAuthorize التي يتم تعليقها من فئات وحدة التحكم الخاصة بك. أو يمكنك استخدام فلتر إجراء مخصص لإجراء التفويض. يجعل MVC من السهل القيام به. لقد نشرت منشورًا بالبريد عن هذا هنا. http://www.bradygaster.com/post/custom-authentication-with-mvc-3.0







iidentity