我应该在哪里把code访问permisions在我的ASP.NET MVC网站? [英] Where should I put code for access permisions in my ASP.NET MVC Site?

查看:213
本文介绍了我应该在哪里把code访问permisions在我的ASP.NET MVC网站?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个ASP.NET MVC的网站,利用存储库模式的访问和修改数据。我的资料库界面是通过它们的构造函数传递给每个控制器。我还使用Ninject注入到 DependencyResolver.SetResolver(我的具体存储库类型)

I have an ASP.NET MVC site which makes use of the repository pattern for accessing and modifying data. My repository interface is passed to each controller through their constructors. I'm also using Ninject to inject my concrete repository type through DependencyResolver.SetResolver().

在我的网站的用户应该只能够访问分配给它们的数据。我试图找出是我应该检查当前用户具​​有执行当前请求的权限?

Users on my site should only be able to access data that is assigned to them. What I'm trying to figure out is where should I check that the current user has permission to perform the current request?

例如,用户可以提交一个删除项目确认书的URL /项目/删除/ 123,这将删除项目ID为123。不过,我不希望用户能够操纵此URL,并最终删除其他用户的项目。

For example the user may submit an delete item confirmation form to the URL /Item/Delete/123, which will delete the Item with ID 123. However, I don't want a user to be able to manipulate this URL and end up deleting another user's Item.

我要补充的用户验证code将控制器,以便第一件事每个操作方法不被检查当前用户甚至拥有他们正试图修改数据?这似乎是它会增加太多的智能控制器应该是相当薄。

Should I add the user validation code to the controllers so the first thing each action method does is check if the current user even owns the data they are trying to modify? This seems like it would be adding too much intelligence to the controller which should be rather thin.

我认为这将更有意义,这种逻辑添加到我的仓库?例如,我可能有一个Repository.GetItem(INT ID,字符串用户),而不是Repository.GetItem(INT ID),除非用户拥有所要求的项目这将抛出一个异常。

I think it would make more sense to add this logic to my repository? For example I may have a Repository.GetItem(int id, string user) rather then Repository.GetItem(int id), which would throw an exception unless "user" owns the requested item.

另外我在想,当它被实例化创建我的仓库的每个实例可以被分配给特定的用户。然后,这些用户特定的存储库将抛出异常,如果有曾经访问或修改不被当前用户所拥有的数据的一种尝试。然后,控制器将只需要赶论文异常和用户重定向到一个错误页面,如果被抓住。

Alternatively I was thinking that each instance of my repository created could be assigned to a specific user when it is instantiated. These user specific repository would then throw exceptions if there is ever an attempt to access or modify data that is not owned by the current user. The controller would then simply need to catch theses exceptions and redirect the user to an error page if one is caught.

推荐答案

我最近遇到了相同的问题就来了。我结束了一个自定义ActionFilter从 AuthorizeAttribute 继承下去。

I recently came across the exact same issue. I ended up going with a custom ActionFilter that inherits from AuthorizeAttribute.

基本上,它具有相同的功能授权(检查是否有用户属于上市角色的至少一个),而且还增加了功能检查,如果用户拥有特定数据。

It basically has the same functionality as Authorize (checks if a user belongs to at least one of the roles listed) but also adds the capability to check if a user "owns" the particular data.

这里的code为你作为一个例子使用。如果有什么不明确,请评论,我会尽力解释。

Here's the code for you to use as an example. If anything is not clear, please comment, and I'll try to explain.

[修改 - 基于瑞安的提议下,我做了 PARAMS的UserRole [] 构造函数的参数,而不是公共财产,并添加 AllowAnyRolesIfNoneSpecified ]

[Edit - Based on Ryan's suggestion, I made params UserRole[] a constructor parameter instead of a public property and added AllowAnyRolesIfNoneSpecified.]

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = false)]
public class AccountAuthorizeAttribute : AuthorizeAttribute
{
    private readonly UserRole[] _userRoles;

    public bool MustBeInRoleOrPageOwner { get; set; }
    public bool MustBeInRoleAndPageOwner { get; set; }
    public bool MustBeInRoleAndNotPageOwner { get; set; }
    public bool AllowAnyRolesIfNoneSpecified { get; set; }
    public string AccessDeniedViewName { get; set; }

    public AccountAuthorizeAttribute(params UserRole[] userRoles)
    {
        _userRoles = userRoles;
        MustBeInRoleOrPageOwner = false;
        MustBeInRoleAndPageOwner = false;
        MustBeInRoleAndNotPageOwner = false;
        AllowAnyRolesIfNoneSpecified = true;
        AccessDeniedViewName = "AccessDenied";
    }

    protected void CacheValidateHandler(HttpContext context, object data, ref HttpValidationStatus validationStatus)
    {
        validationStatus = OnCacheAuthorization(new HttpContextWrapper(context));
    }

    public override void OnAuthorization(AuthorizationContext filterContext)
    {
        if (!filterContext.HttpContext.User.Identity.IsAuthenticated)
        {
            ShowLogOnPage(filterContext);
            return;
        }
        using (var dbContext = new MainDbContext())
        {
            var accountService = new AccountService(dbContext);
            var emailAddress = filterContext.HttpContext.User.Identity.Name;
            if (IsUserInRole(accountService, emailAddress))
            {
                var isPageOwner = IsUserPageOwner(filterContext, dbContext, accountService, emailAddress);
                if (MustBeInRoleAndPageOwner && !isPageOwner || MustBeInRoleAndNotPageOwner && isPageOwner)
                    ShowAccessDeniedPage(filterContext);
            }
            else
            {
                if (!MustBeInRoleOrPageOwner)
                    ShowAccessDeniedPage(filterContext);
                else if (!IsUserPageOwner(filterContext, dbContext, accountService, emailAddress))
                    ShowAccessDeniedPage(filterContext);
            }
        }
    }

    private bool IsUserInRole(AccountService accountService, string emailAddress)
    {
        if (_userRoles.Length == 0 && AllowAnyRolesIfNoneSpecified) return true;
        return accountService.IsUserInRole(emailAddress, _userRoles);
    }

    protected virtual bool IsUserPageOwner(
        AuthorizationContext filterContext, MainDbContext dbContext, AccountService accountService, string emailAddress)
    {
        var id = GetRouteId(filterContext);
        return IsUserPageOwner(dbContext, accountService, emailAddress, id);
    }

    protected int GetRouteId(AuthorizationContext filterContext)
    {
        return Convert.ToInt32(filterContext.RouteData.Values["id"]);
    }

    private bool IsUserPageOwner(MainDbContext dbContext, AccountService accountService, string emailAddress, int id)
    {
        return accountService.IsUserPageOwner(emailAddress, id);
    }

    private void ShowLogOnPage(AuthorizationContext filterContext)
    {
        filterContext.Result = new HttpUnauthorizedResult();
    }

    private void ShowAccessDeniedPage(AuthorizationContext filterContext)
    {
        filterContext.Result = new ViewResult { ViewName = "ErrorPages/" + AccessDeniedViewName };
    }

    private void PreventPageFromBeingCached(AuthorizationContext filterContext)
    {
        var cachePolicy = filterContext.HttpContext.Response.Cache;
        cachePolicy.SetProxyMaxAge(new TimeSpan(0));
        cachePolicy.AddValidationCallback(CacheValidateHandler, null);
    }
}

一对夫妇的注意事项:

A couple notes:

要避免魔串,我用的UserRole的数组枚举值,而不是一个字符串。另外,我建立了它处理几种情况我遇到:

To avoid "magic strings", I used an array of UserRole enum values instead of a single string. Also, I built it to handle several scenarios I came across:


  • 用户必须在角色的的页面/数据的拥有者(例如,管理员可以编辑任何人的资料,任何人都可以编辑自己的数据)

  • 用户必须在角色的的页面/数据的拥有者(例如,一个用户只能编辑他/她自己的页面/数据 - 通常没有任何角色限制使用)

  • 用户必须在角色的的页面/数据的拥有者(例如,管理员除了他自己可以编辑任何人的页面/数据 - 例如,以prevent管理员从删除自己的帐号)

  • 否允许用户浏览这个页面, AllowAnyRolesIfNoneSpecified = FALSE (例如,你有一个不存在的页面控制器方法,但你需要包括方法,因为您的控制器从具有这种方法的基类继承)

  • User must be in a role or the owner of the page/data (e.g., an admin can edit anyone's data and anyone can edit their own data)
  • User must be in a role and the owner of the page/data (e.g., a user can only edit his/her own page/data -- usually used without any role restrictions)
  • User must be in a role and not the owner of the page/data (e.g., an admin can edit anyone's page/data except his own -- say, to prevent an admin from deleting his own account)
  • No user is allowed to view this page, AllowAnyRolesIfNoneSpecified = false (e.g., you have a controller method for a page that doesn't exist but you need to include the method because your controller inherits from a base class that has this method)

下面是一个例子属性声明:

Here's an example attribute declaration:

[AccountAuthorize(UserRole.Admin, MustBeInRoleAndNotPageOwner = true)]
public override ActionResult DeleteConfirmed(int id)
{
    ...
}

(这意味着管理员可以删除任何帐户,但他自己的。)

(This means an admin can delete any account but his own.)

这篇关于我应该在哪里把code访问permisions在我的ASP.NET MVC网站?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆