Многопользовательское приложение с MVC3, членство в ASP.NET - аутентификация пользователя / разделение данных - PullRequest
5 голосов
/ 04 марта 2011

Я создаю простое многопользовательское (многопользовательское?) Приложение с ASP.NET MVC3 и EF4, одной базой данных, одной кодовой базой, все пользователи получают доступ к приложению по одному и тому же URL. Как только пользователь вошел в систему, он должен иметь доступ только к своим данным, я использую поставщика членства asp.NET по умолчанию и добавил поле Guid «UserId» в каждой из таблиц данных. Очевидно, я не хочу, чтобы пользователь А имел какой-либо доступ к данным пользователя Б, поэтому я добавил следующее почти к каждому действию на моих контроллерах.

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;
    }

Это повторение определенно кажется неправильным, и должен быть более элегантный способ гарантировать, что мои пользователи не смогут получить доступ к данным друг друга - что мне не хватает?

Ответы [ 2 ]

12 голосов
/ 04 марта 2011

что мне не хватает?

Папка для пользовательских моделей:

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
    ...
}

Вывод: мы поставили этот контроллер на диету, которая должна быть такой, какой должны быть контроллеры: -)

1 голос
/ 29 ноября 2011

Пытаясь разобраться с этой реализацией (у меня точно такой же вопрос), я нашел похожий подход, описанный в посте Скотта Хансельмана

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);
    }

Для меня, новичка в MVC, это было несколько легче понять.

...