具有 MVC3、ASP.NET 成员资格的多用户应用程序 - 用户身份验证/数据分离

发布于 2024-10-20 20:13:02 字数 944 浏览 5 评论 0原文

我正在使用 ASP.NET MVC3 和 EF4、一个数据库、一个代码库构建一个简单的多用户(多租户?)应用程序,所有用户都使用相同的 URL 访问该应用程序。用户登录后,他们应该只能访问自己的数据,我使用默认的 asp.NET 成员资格提供程序,并在每个数据表上添加了一个“UserId”Guid 字段。显然,我不希望用户 A 能够访问用户 B 的数据,因此我将以下内容添加到控制器上的几乎每个操作中。

public ActionResult EditStatus(int id)
    {
        if (!Request.IsAuthenticated)
            return RedirectToAction("Index", "Home");

        var status = sService.GetStatusById(id);

        // check if the logged in user has access to this status
        if (status.UserId != GetUserId())
            return RedirectToAction("Index", "Home");
    .
    .
    .
    }

    private Guid GetUserId()
    {
        if (Membership.GetUser() != null)
        {
            MembershipUser member = Membership.GetUser();
            Guid id = new Guid(member.ProviderUserKey.ToString());
            return id;
        }
        return Guid.Empty;
    }

这种重复肯定感觉不对,必须有一种更优雅的方法来确保我的用户无法访问彼此的数据 - 我错过了什么?

I'm building a simple multi-user (multi-tenant?) App with ASP.NET MVC3 and EF4, one database, one code base, all users access the app using the same URL. Once a User is logged in they should only have access to their data, I'm using the default asp.NET membership provider and have added a ‘UserId’ Guid field on each of the data tables. Obviously I don't want user A to have any access to user B’s data so I have been adding the following to nearly every action on my controllers.

public ActionResult EditStatus(int id)
    {
        if (!Request.IsAuthenticated)
            return RedirectToAction("Index", "Home");

        var status = sService.GetStatusById(id);

        // check if the logged in user has access to this status
        if (status.UserId != GetUserId())
            return RedirectToAction("Index", "Home");
    .
    .
    .
    }

    private Guid GetUserId()
    {
        if (Membership.GetUser() != null)
        {
            MembershipUser member = Membership.GetUser();
            Guid id = new Guid(member.ProviderUserKey.ToString());
            return id;
        }
        return Guid.Empty;
    }

This repetition is definitely feeling wrong and there must be a more elegant way of ensuring my users can't access each other's data – what am I missing?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

与他有关 2024-10-27 20:13:02

我错过了什么?

自定义模型绑定器:

public class StatusModelBinder : DefaultModelBinder
{
    public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        // Fetch the id from the RouteData
        var id = controllerContext.RouteData.Values["id"] as string;

        // TODO: Use constructor injection to pass the service here
        var status = sService.GetStatusById(id);

        // Compare whether the id passed in the request belongs to 
        // the currently logged in user
        if (status.UserId != GetUserId())
        {
            throw new HttpException(403, "Forbidden");
        }
        return status;
    }

    private Guid GetUserId()
    {
        if (Membership.GetUser() != null)
        {
            MembershipUser member = Membership.GetUser();
            Guid id = new Guid(member.ProviderUserKey.ToString());
            return id;
        }
        return Guid.Empty;
    }
}

然后您将在 Application_Start 中注册此模型绑定器:

// Could use constructor injection to pass the repository to the model binder
ModelBinders.Binders.Add(typeof(Status), new StatusModelBinder());

最后

// The authorize attribute ensures that a user is authenticated. 
// If you want it to redirect to /Home/Index as in your original
// example if the user is not authenticated you could write a custom
// Authorize attribute and do the job there
[Authorize]
public ActionResult EditStatus(Status status)
{
    // if we got that far it means that the user has access to this resource
    // TODO: do something with the status and return some view
    ...
}

结论:我们已经将此控制器放在了节食上,这就是控制器应有的方式:-)

what am I missing?

A custom model binder:

public class StatusModelBinder : DefaultModelBinder
{
    public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        // Fetch the id from the RouteData
        var id = controllerContext.RouteData.Values["id"] as string;

        // TODO: Use constructor injection to pass the service here
        var status = sService.GetStatusById(id);

        // Compare whether the id passed in the request belongs to 
        // the currently logged in user
        if (status.UserId != GetUserId())
        {
            throw new HttpException(403, "Forbidden");
        }
        return status;
    }

    private Guid GetUserId()
    {
        if (Membership.GetUser() != null)
        {
            MembershipUser member = Membership.GetUser();
            Guid id = new Guid(member.ProviderUserKey.ToString());
            return id;
        }
        return Guid.Empty;
    }
}

and then you would register this model binder in Application_Start:

// Could use constructor injection to pass the repository to the model binder
ModelBinders.Binders.Add(typeof(Status), new StatusModelBinder());

and finally

// The authorize attribute ensures that a user is authenticated. 
// If you want it to redirect to /Home/Index as in your original
// example if the user is not authenticated you could write a custom
// Authorize attribute and do the job there
[Authorize]
public ActionResult EditStatus(Status status)
{
    // if we got that far it means that the user has access to this resource
    // TODO: do something with the status and return some view
    ...
}

Conclusion: We've put this controller on a diet which is the way controllers should be :-)

梨涡 2024-10-27 20:13:02

试图弄清楚这个实现(我有完全相同的问题),我发现了 Scott Hanselman 的文章


    public class IPrincipalModelBinder : IModelBinder
    {    
        public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)    
        {        
            if (controllerContext == null) 
            {            
            throw new ArgumentNullException("controllerContext");        
            }        
            if (bindingContext == null) 
            {            
            throw new ArgumentNullException("bindingContext");        
            }        
            IPrincipal p = controllerContext.HttpContext.User;        
            return p;    
        }
    }



    void Application_Start() 
    {    
        RegisterRoutes(RouteTable.Routes); //unrelated, don't sweat this line.    
        ModelBinders.Binders[typeof(IPrincipal)] = new IPrincipalModelBinder();
    }

    [Authorize]
    public ActionResult Edit(int id, IPrincipal user) 
    {     
        Dinner dinner = dinnerRepository.FindDinner(id);     

        if (dinner.HostedBy != user.Identity.Name)        
            return View("InvalidOwner");     

        var viewModel = new DinnerFormViewModel {        
            Dinner = dinner,        
            Countries = new SelectList(PhoneValidator.Countries, dinner.Country)    
        };     
        return View(viewModel);
    }

的 MVC 菜鸟来说,这更容易理解。

Trying to get my head around this implementation (I'm having exactly the same question), I found a similar approach described in Scott Hanselman't post

http://www.hanselman.com/blog/IPrincipalUserModelBinderInASPNETMVCForEasierTesting.aspx


    public class IPrincipalModelBinder : IModelBinder
    {    
        public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)    
        {        
            if (controllerContext == null) 
            {            
            throw new ArgumentNullException("controllerContext");        
            }        
            if (bindingContext == null) 
            {            
            throw new ArgumentNullException("bindingContext");        
            }        
            IPrincipal p = controllerContext.HttpContext.User;        
            return p;    
        }
    }



    void Application_Start() 
    {    
        RegisterRoutes(RouteTable.Routes); //unrelated, don't sweat this line.    
        ModelBinders.Binders[typeof(IPrincipal)] = new IPrincipalModelBinder();
    }

    [Authorize]
    public ActionResult Edit(int id, IPrincipal user) 
    {     
        Dinner dinner = dinnerRepository.FindDinner(id);     

        if (dinner.HostedBy != user.Identity.Name)        
            return View("InvalidOwner");     

        var viewModel = new DinnerFormViewModel {        
            Dinner = dinner,        
            Countries = new SelectList(PhoneValidator.Countries, dinner.Country)    
        };     
        return View(viewModel);
    }

For a total MVC noob as myself, that was somewhat easier to understand.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文