ASP.NET MVC مع LINQ إلى SQL:كيفية تنفيذ التفويض للجهات الموجودة في المستودع؟

StackOverflow https://stackoverflow.com/questions/1429661

سؤال

مع وضع التصميم المعتمد على المجال في الاعتبار، كيف يمكنك تنفيذ ترخيص المستخدم في المستودع؟على وجه التحديد، كيف يمكنك تقييد البيانات التي يمكنك رؤيتها من خلال تسجيل الدخول المقدم من المستخدم؟

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

أسئلة:

  1. هل ستختار جميع المنتجات في الريبو، ثم تستخدم مرشحًا لتقييد المنتجات التي يتم إرجاعها؟ Like GetProducts("keyword: boat").restrictBy("myusername")?
  2. هل ستقرأ تسجيل الدخول من سياق وحدة التحكم داخل المستودع وتصفية النتائج بشكل سلبي؟
  3. كيف يمكنك تخزين العلاقة بين دور المستخدم والكيانات التي يمكنه الوصول إليها؟هل يمكنك ببساطة تخزين مفتاح الكيان والدور في جدول متعدد إلى متعدد، مع وجود سجل واحد لكل منتج يمكن لكل مدير متجر الوصول إليه؟

ستكون أمثلة التعليمات البرمجية أو الروابط لأمثلة التعليمات البرمجية رائعة.شكرًا لك.

هل كانت مفيدة؟

المحلول

الطريقة التي اتخذتها هي استخدام السمات في إجراء وحدة التحكم الذي يفحص العلاقة بين المستخدم الحالي والكيان المطلوب، ثم يسمح بالإجراء أو لا يسمح به بناءً على نتائج البحث.لديّ سمتان مختلفتان اعتمادًا على ما إذا كانت تمر عبر جدول ربط أو لها علاقة مباشرة.إنه يستخدم الانعكاس مقابل سياق البيانات، في حالتي، ولكن في المستودع (المستودعات) الخاص بك للحصول على القيم والتحقق من تطابقها.سأقوم بتضمين الكود أدناه (الذي بذلت بعض الجهود لتعميمه حتى لا يتم تجميعه).لاحظ أنه يمكنك توسيع هذا ليشمل بعض مفهوم الإذن أيضًا (في جدول الانضمام).

رمز لسمة العلاقة المباشرة.إنه يتحقق من أن المستخدم الحالي هو مالك السجل (السمة "المعرف" المحددة في معلمات التوجيه تتطابق مع معرف المستخدم الحالي في جدول المستخدم).

[AttributeUsage( AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = false )]
public class RoleOrOwnerAuthorizationAttribute : AuthorizationAttribute
{
    private IDataContextFactory ContextFactory { get; set; }

    private string routeParameter = "id";
    /// <summary>
    /// The name of the routing parameter to use to identify the owner of the data (participant id) in question.  Default is "id".
    /// </summary>
    public string RouteParameter
    {
        get { return this.routeParameter; }
        set { this.routeParameter = value; }
    }

    public RoleOrOwnerAuthorizationAttribute()
        : this( null )
    {
    }

    // this is for unit testing support
    public RoleOrOwnerAuthorizationAttribute( IDataContextFactory factory )
    {
        this.ContextFactory = factory ?? DefaultFactory();
    }

    public override void OnAuthorization( AuthorizationContext filterContext )
    {
        if (filterContext == null)
        {
            throw new ArgumentNullException( "filterContext" );
        }

        if (AuthorizeCore( filterContext.HttpContext ))
        {
            SetCachePolicy( filterContext );
        }
        else if (!filterContext.HttpContext.User.Identity.IsAuthenticated)
        {
            // auth failed, redirect to login page
            filterContext.Result = new HttpUnauthorizedResult();
        }
        else if (filterContext.HttpContext.User.IsInRole( "SuperUser" ) || IsOwner( filterContext ))
        {
            SetCachePolicy( filterContext );
        }
        else
        {
            ViewDataDictionary viewData = new ViewDataDictionary();
            viewData.Add( "Message", "You do not have sufficient privileges for this operation." );
            filterContext.Result = new ViewResult { MasterName = this.MasterName, ViewName = this.ViewName, ViewData = viewData };
        }

    }

    private bool IsOwner( AuthorizationContext filterContext )
    {
        using (var dc = this.ContextFactory.GetDataContextWrapper())
        {
            int id = -1;
            if (filterContext.RouteData.Values.ContainsKey( this.RouteParameter ))
            {
                id = Convert.ToInt32( filterContext.RouteData.Values[this.RouteParameter] );
            }

            string userName = filterContext.HttpContext.User.Identity.Name;

            return dc.Table<UserTable>().Where( p => p.UserName == userName && p.ParticipantID == id ).Any();
        }

    }

}

هذا هو رمز سمة الاقتران، أي يوجد ارتباط في جدول الانضمام بين المعرف الموجود في معلمة التوجيه ومعرف المستخدم من جدول المستخدم.لاحظ أن هناك تبعية على كود System.Linq.Dynamic من ملف عينات VS2008.

[AttributeUsage( AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = false )]
public class RoleOrOwnerAssociatedAuthorizationAttribute : MasterEventAuthorizationAttribute
{
    private IDataContextFactory ContextFactory { get; set; }

    public RoleOrOwnerAssociatedAuthorizationAttribute()
        : this( null )
    {
    }

    // this supports unit testing
    public RoleOrOwnerAssociatedAuthorizationAttribute( IDataContextFactory factory )
    {
        this.ContextFactory = factory ?? new DefaultDataContextFactory();
    }

    /// <summary>
    /// The table in which to find the current user by name.
    /// </summary>
    public string UserTable { get; set; }
    /// <summary>
    /// The name of the property in the UserTable that holds the user's name to match against
    /// the current context's user name.
    /// </summary>
    public string UserNameProperty { get; set; }
    /// <summary>
    /// The property to select from the UserTable to match against the UserEntityProperty on the JoinTable
    /// to determine membership.
    /// </summary>
    public string UserSelectionProperty { get; set; }
    /// <summary>
    /// The join table that links users and the entity table.  An entry in this table indicates
    /// an association between the user and the entity.
    /// </summary>
    public string JoinTable { get; set; }
    /// <summary>
    /// The property on the JoinTable used to hold the entity's key.
    /// </summary>
    public string EntityProperty { get; set; }
    /// <summary>
    /// The property on the JoinTable used to hold the user's key.
    /// </summary>
    public string UserEntityProperty { get; set; }
    /// <summary>
    /// The name of the route data parameter which holds the group's key being requested.
    /// </summary>
    public string RouteParameter { get; set; }

    public override void OnAuthorization( AuthorizationContext filterContext )
    {
        using (var dc = this.ContextFactory.GetDataContextWrapper())
        {
            if (filterContext == null)
            {
                throw new ArgumentNullException( "filterContext" );
            }

            if (AuthorizeCore( filterContext.HttpContext ))
            {
                SetCachePolicy( filterContext );
            }
            else if (!filterContext.HttpContext.User.Identity.IsAuthenticated)
            {
                // auth failed, redirect to login page
                filterContext.Result = new HttpUnauthorizedResult();
            }
            else if (filterContext.HttpContext.User.IsInRole( "SuperUser" )
                     || IsRelated( filterContext, this.GetTable( dc, this.JoinTable ), this.GetTable( dc, this.UserTable ) ))
            {
                SetCachePolicy( filterContext );
            }
            else
            {
                ViewDataDictionary viewData = new ViewDataDictionary();
                viewData.Add( "Message", "You do not have sufficient privileges for this operation." );
                filterContext.Result = new ViewResult { MasterName = this.MasterName, ViewName = this.ViewName, ViewData = viewData };
            }
        }
    }

    protected bool IsRelated( AuthorizationContext filterContext, IQueryable joinTable, IQueryable userTable )
    {
        bool result = false;
        try
        {
            int entityIdentifier = Convert.ToInt32( filterContext.RouteData.Values[this.RouteParameter] );
            int userIdentifier = this.GetUserIdentifer( filterContext, userTable );

            result = joinTable.Where( this.EntityProperty + "=@0 and " + this.UserEntityProperty + "=@1",
                                      entityIdentifier,
                                      userIdentifier )
                              .Count() > 0;
        }
        catch (NullReferenceException) { }
        catch (ArgumentNullException) { }
        return result;
    }

    private int GetUserIdentifer( AuthorizationContext filterContext, IQueryable userTable )
    {
        string userName = filterContext.HttpContext.User.Identity.Name;

        var query = userTable.Where( this.UserNameProperty + "=@0", userName )
                             .Select( this.UserSelectionProperty );

        int userIdentifer = -1;
        foreach (var value in query)
        {
            userIdentifer = Convert.ToInt32( value );
            break;
        }
        return userIdentifer;
    }

    private IQueryable GetTable( IDataContextWrapper dc, string name )
    {
        IQueryable result = null;
        if (!string.IsNullOrEmpty( name ))
        {
            DataContext context = dc.GetContext<DefaultDataContext>();
            PropertyInfo info = context.GetType().GetProperty( name );
            if (info != null)
            {
                result = info.GetValue( context, null ) as IQueryable;
            }
        }
        return result;
    }
}

نصائح أخرى

سألت جدا سؤال مماثل على ال مجموعة أخبار S#arp للهندسة المعمارية لأي منهم توم كابانسكي اقترح إطار عمل AOP، مثل بوستشارب.يبدو هذا حلاً عمليًا لاحتياجاتي، لذا أخطط لإلقاء نظرة أكثر تعمقًا عليه.لسوء الحظ، هذه ليست إجابة كاملة لسؤالك، حيث ليس لدي أمثلة على التعليمات البرمجية لمشاركتها.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top