如何在我的DDD模型中将"用户"与认证用户集成?

Eri*_*tas 39 asp.net authentication asp.net-mvc domain-driven-design

我正在创建我的第一个ASP.NET MVC站点,并一直在尝试遵循域驱动的开发.我的站点是一个项目协作站点,可以将用户分配到站点上的一个或多个项目.然后将任务添加到项目中,并将项目中的用户分配给任务.所以"用户"是我的域模型的基本概念.

我的计划是拥有一个"用户"模型对象,其中包含有关用户的所有信息,可以通过IUserRepository访问.UserId可以识别每个用户.虽然我现在还不确定我是否希望UserId是一个字符串或整数.

我的域对象User和IUserRepository应该如何与我的网站的更多管理功能相关,例如授权用户并允许他们登录?如何将我的域模型与ASP.NET的其他方面集成,例如HttpContext.User,HttpContext.Profile,自定义MemberShipProvider,自定义ProfileProvider或自定义AuthorizeAttribute?

我应该创建一个自定义MembershipProvider和/或包含我的IUserRepository的ProfileProvider吗?虽然,我也可以预见为什么我可能想要将我的域模型中的用户信息与我站点上的用户授权分开.例如,将来我可能希望从表单身份验证切换到Windows身份验证.

不尝试重新发明轮子并坚持使用ASP.NET内置的标准SqlMembershipProvider会更好吗?每个用户的个人资料信息都将存储在域模型(User/IUserRepository)中,但这不包括他们的密码.然后我会使用标准的ASP.NET成员资格来处理创建和授权用户?因此,在创建帐户或第一次登录时,需要在某个地方创建一些代码,以便为IUserRepository中的新用户创建配置文件.

RPM*_*984 15

是的 - 非常好的问题.和@Andrew Cooper一样,我们的团队也经历了这一切.

我们采用以下方法(对或错):

定制会员提供商

我或其他开发人员都不是内置ASP.NET成员资格提供程序的粉丝.对于我们的网站来说,它太过臃肿了(简单的,由UGC驱动的社交网站).我们创建了一个非常简单的应用程序,它可以满足我们的应用需求,仅此而已 虽然内置会员提供商可以提供您可能需要的所有内容,但很可能不会.

自定义表单身份验证票证/身份验证

我们的应用程序中的所有内容都使用接口驱动的依赖注入(StructureMap).这包括表单身份验证.我们创建了一个非常薄的界面:

public interface IAuthenticationService
{
   void SignIn(User user, HttpResponseBase httpResponseBase);
   void SignOut();
}
Run Code Online (Sandbox Code Playgroud)

这个简单的界面可以轻松进行模拟/测试.通过实现,我们创建了一个自定义表单身份验证票证,其中包含:每个HTTP请求都需要的UserId和Roles等内容,不经常更改,因此不应在每个请求中获取.

然后,我们使用动作过滤器来解密表单身份验证票证(包括角色)并将其粘贴到HttpContext.Current.User.Identity(我们的Principal对象也基于接口)中.

使用[授权]和[AdminOnly]

我们仍然可以使用MVC中的授权属性.我们还为每个角色创建了一个.[AdminOnly]只需检查当前用户的角色,然后抛出401(禁止).

用户简单的单表,简单的POCO

所有用户信息都存储在一个表中("可选"用户信息除外,例如配置文件兴趣).这被映射到一个简单的POCO(实体框架),它也具有内置于对象中的域逻辑.

用户存储库/服务

特定于域的简单用户存储库.更改密码,更新配置文件,检索用户等等.存储库调用上面提到的User对象的域逻辑.该服务是存储库顶部的一个瘦包装器,它将单个存储库方法(例如Find)分离为更专业的方法(FindById,FindByNickname).

域名与安全分离

我们的"域"用户和他/她的关联信息.这包括姓名,个人资料,脸书/社交整合等.

像"登录","注销"之类的东西正在处理身份验证,"User.IsInRole"之类的东西处理授权,因此不属于域.

因此我们的控制器可以同时使用IAuthenticationServiceIUserService.

创建配置文件是域逻辑的完美示例,也与身份验证逻辑混合在一起.

这是我们的样子:

[HttpPost]
[ActionName("Signup")]
public ActionResult Signup(SignupViewModel model)
{
    if (ModelState.IsValid)
    {
        try
        {
            // Map to Domain Model.
            var user = Mapper.Map<SignupViewModel, Core.Entities.Users.User>(model);

            // Create salt and hash password.           
            user.Password = _authenticationService.SaltAndHashPassword();

            // Signup User.
            _userService.Save(user);

            // Save Changes.
            _unitOfWork.Commit();

            // Forms Authenticate this user.
            _authenticationService.SignIn(user, Response);

            // Redirect to homepage.
            return RedirectToAction("Index", "Home", new { area = "" });
        }
        catch (Exception exception)
        {
            ModelState.AddModelError("SignupError", "Sorry, an error occured during Signup. Please try again later.");
            _loggingService.Error(exception);
        }
    }

    return View(model);
}
Run Code Online (Sandbox Code Playgroud)

摘要

以上对我们来说效果很好.我喜欢有一个简单的用户表,而不是那种臃肿的疯狂,即ASP.NET成员资格提供者.它很简单,代表我们的,而不是ASP.NET的代表.

话虽如此,正如我所说,我们有一个简单的网站.如果你正在银行网站上工作,那么我会小心重新发明轮子.

我的建议是在你考虑认证之前先创建你的域/模型.(当然,这就是DDD的全部意义).

然后计算出您的安全要求,并选择适当的身份验证提供程序(现成的或自定义的).

不要让ASP.NET决定如何设计您的域.这是大多数人陷入的陷阱(包括我,在以前的项目中).

祝好运!