如何将我的DDD模型中的“用户”与validation用户进行集成?

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

我的计划是有一个“用户”模型对象,其中包含有关用户的所有信息,可以通过IUserRepository访问。 每个用户都可以通过UserId来识别。 虽然我不确定在这一点上,如果我想UserId是一个string或整数。

我的域对象应如何与用户和IUserRepository相关的更多我的网站的pipe理function,如授权用户,并允许他们login? 如何将我的域模型与ASP.NET的其他方面(如HttpContext.User,HttpContext.Profile,自定义MemberShipProvider,自定义ProfileProvider或自定义AuthorizeAttribute?

我应该创build一个自定义MembershipProvider和/或ProfileProvider包装我的IUserRepository? 虽然,我也可以预见为什么我可能希望将我的域模型中的用户信息与我的网站上的用户授权分开。 例如在将来我可能想从窗体身份validation切换到Windows身份validation。

最好不要试图重新发明轮子,并坚持使用内置于ASP.NET的标准SqlMembershipProvider? 每个用户的configuration文件信息都将存储在域模型(User / IUserRepository)中,但不包括其密码。 然后,我会使用标准的ASP.NET成员资格来处理创build和授权用户? 因此,需要在某处添加一些代码,以便在创build帐户或第一次login时为IUserRepository中的新用户创buildconfiguration文件。

是的 – 非常好的问题。 就像@Andrew Cooper一样,我们的团队也经历了这一切。

我们采用以下方法(正确或错误):

自定义会员供应商

我或其他开发人员都不是内置ASP.NET成员资格提供程序的粉丝。 我们网站的内容太臃肿(简单,由UGC驱动的社交网站)。 我们创build了一个非常简单的应用程序需要的东西,除此之外, 而内置的会员供应商可以处理您可能需要的一切,但很可能不会。

自定义表单authentication票证/authentication

我们应用程序中的所有东西都使用接口驱动的dependency injection(StructureMap)。 这包括表单身份validation。 我们创build了一个非常薄的界面:

public interface IAuthenticationService { void SignIn(User user, HttpResponseBase httpResponseBase); void SignOut(); } 

这个简单的界面允许轻松模拟/testing。 通过实现,我们创build了一个自定义表单身份validation票据,其中包含:每个HTTP请求所需的UserId和Roles等内容,不会频繁更改,因此不应在每个请求中提取。

然后,我们使用动作filter来解密表单authentication票据(包括angular色)并将其粘贴到HttpContext.Current.User.Identity (我们的Principal对象也是基于接口的)。

使用[Authorize]和[AdminOnly]

我们仍然可以使用MVC中的授权属性。 而且我们还为每个angular色创build了一个。 [AdminOnly]仅检查当前用户的angular色,并引发401(禁止)。

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

所有用户信息都存储在一个表中(除“可选”用户信息外,如configuration文件兴趣)。 这被映射到一个简单的POCO(entity framework),它也有对象内置的域逻辑。

用户库/服务

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

域名与安全分离

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

像“login”,“注销”等事情处理authentication ,像“User.IsInRole”处理授权 ,因此不属于域。

所以我们的控制器可以同时使用IAuthenticationServiceIUserService

创buildconfiguration文件是域逻辑的一个很好的例子,它也与authentication逻辑混合在一起。

这是我们的样子:

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

概要

以上对我们运作良好。 我喜欢有一个简单的用户表,而不是臃肿的疯狂是ASP.NET会员提供。 这很简单,代表我们的领域 ,而不是ASP.NET的表示。

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

我build议使用的是先创build你的域/模型,然后再考虑authentication。 (当然,这是DDD的全部内容)。

然后制定您的安全要求,并适当地select一个身份validation提供程序(现成的或自定义的)。

不要让ASP.NET指定您的域名应该如何devise。 这是大多数人陷入的陷阱(包括我在之前的项目中)。

祝你好运!

让我分解一下你的一些问题:

虽然我不确定在这一点上,如果我希望UserId是一个string或整数。

它不一定是一个整数,但肯定在这里使用某种基于位的值(如int,long或guid)。 在固定大小的值上运行的索引比string上的索引要快得多,并且在您的一生中,您永远不会用完用户的标识符。

我的域对象应如何与用户和IUserRepository相关的更多我的网站的pipe理function,如授权用户,并允许他们login?

决定是否要使用内置的asp.net成员资格。 我build议不要因为它大部分是膨胀的原因,而且你必须自己实现它的大部分function,比如电子邮件validation,你可以通过查看生成的表来生成它。这个模板ASP.NET MVC 1和2的项目都包含一个简单的成员资源库,只需重写实际validation用户的函数,就可以顺利完成任务。

我如何将我的域模型与ASP.NET的其他方面(如HttpContext.User,HttpContext.Profile,自定义MemberShipProvider,自定义ProfileProvider或自定义AuthorizeAttribute?

其中每一个都值得它自己的问题,每一个都在这里被问到。 这就是说,HttpContext.User只有在使用内置的FormsAuthenticationfunction时才有用,而且我build议在开始之前使用它,直到遇到不符合要求的情况。 如果HttpContext.User.IsAuthenticatedtrue那么在使用FormsAuthenticationlogin时,我喜欢将用户密钥存储在名称中,并在每个请求的开始处加载请求绑定的当前用户对象。

至于简介,我有激情地避免有状态的请求,以前从来没有使用过,所以别人将不得不帮助你。

所有你需要使用内置的[Authorize]属性是告诉FormsAuthentication用户是否已经进行了authentication。 如果你想使用authorize属性的angular色function,写你自己的RoleProvider ,它会像魔术一样工作。 你可以在堆栈溢出find大量的例子。 HACK:你只需要实现RoleProvider.GetAllRoles()RoleProvider.GetRolesForUser(string username)RoleProvider.IsUserInRole(string username, string roleName) 。 除非你想使用asp.net成员系统的所有function,否则你不必实现整个接口。

最好不要试图重新发明轮子,并坚持使用内置于ASP.NET的标准SqlMembershipProvider?

对于这个问题的每一个推导的务实答案是不要重新发明轮子,直到轮子不能做你需要做的事情,你需要做什么。

 if (built in stuff works fine) { use the built in stuff; } else { write your own; } if (easier to write your own then figure out how to use another tool) { write your own; } else { use another tool; } if (need feature not in the system) { if (time to extend existing api < time to do it yourself) { extend api; } else { do it yourself; } } 

我知道我的答案迟了一点,但是为了将来提到有同样问题的其他同事。

以下是使用angular色的自定义身份validation和授权示例。 http://www.codeproject.com/Articles/408306/Understanding-and-Implementing-ASP-NET-Custom-Form 。 这是一篇非常好的文章,非常新鲜和最近。

在我看来,你应该有这个实现作为基础设施的一部分(只需创build一个新的项目安全或任何你想调用它),并在上面实现这个例子。 然后从你的应用层调用这个机制。 请记住,应用程序层控制和编排您的应用程序中的整个操作。 域层应该只关心业务操作,而不关心访问或数据持久性等等。对你如何在你的系统中validation人是无知的。

想想一个砖头和灰浆公司。 实施的指纹接入系统与该公司的业务无关,但仍是基础设施(build筑)的一部分。 事实上,它控制着谁可以进入公司,所以他们可以完成各自的任务。 你没有两名雇员,一名扫描他的指纹,另一名可以进入工作。 你只有一个食指的员工。 对于“访问”,所有你需要的是他的手指…因此,如果你打算使用相同的UserRepository进行authentication,你的仓库应该包含一个authentication方法。 如果您决定使用AccessService(这是一个应用程序服务,而不是一个域),则需要包含UserRepository,以便访问该用户数据,获取他的手指信息(用户名和密码),并将其与来自表格(手指扫描)。 我解释自己是对的吗?

DDD的大部分情况适用于现实生活中的情况…当涉及到软件的体系结构时。