1.2 ABPZero - 安装

1.2.1 从模板创建

使用ABP和module-zero开始一个新项目最简单的方式是使用启动模板。详细了解请参考启动模板文档

1.2.2 手动安装

如果你有一个预先创建的应用程序,稍后再安装module-zero,您可以按照本部分的说明。

在本文中,我将假定您的解决方案具有以下项目:

  • AbpZeroSample.Core

  • AbpZeroSample.Application

  • AbpZeroSample.EntityFramework

  • AbpZeroSample.Web

  • AbpZeroSample.WebApi

1.2.3 核心(领域)层

安装Abp.Zero的NuGet包到时Core工程。然后进入到核心模块类(在此示例中为AbpZeroSampleCoreModule类),并添加DependsOn属性AbpZeroCoreModule如下图所示:

  1. [DependsOn(typeof(AbpZeroCoreModule))]
  2. public class AbpZeroSampleCoreModule : AbpModule
  3. {
  4. public override void Initialize()
  5. {
  6. IocManager.RegisterAssemblyByConvention(Assembly.GetExecutingAssembly());
  7. }
  8. }

1.2.4 领域类(实体)

Module-zero提供了 User,Role和Tenant 类的抽象。所以,我们应该实现它们,如下所示:

  1. public class User : AbpUser<Tenant, User>
  2. {
  3. }
  4. public class Role : AbpRole<Tenant, User>
  5. {
  6. }
  7. public class Tenant : AbpTenant<Tenant, User>
  8. {
  9. }

你可以在这里添加自定义属性。通过这种方式,我们可以根据我们的需要扩展基用户,角色和租户类。

1.2.5 管理类(领域服务)

我们应该先实现管理基类和存储基类,因为他们是抽象的。

从启动用户存储和用户管理开始:

  1. public class UserStore : AbpUserStore<Tenant, Role, User>
  2. {
  3. public UserStore(
  4. IRepository<User, long> userRepository,
  5. IRepository<UserLogin, long> userLoginRepository,
  6. IRepository<UserRole, long> userRoleRepository,
  7. IRepository<Role> roleRepository,
  8. IRepository<UserPermissionSetting, long> userPermissionSettingRepository,
  9. IUnitOfWorkManager unitOfWorkManager
  10. )
  11. : base(
  12. userRepository,
  13. userLoginRepository,
  14. userRoleRepository,
  15. roleRepository,
  16. userPermissionSettingRepository,
  17. unitOfWorkManager
  18. )
  19. {
  20. }
  21. }
  22. public class UserManager : AbpUserManager<Tenant, Role, User>
  23. {
  24. public UserManager(
  25. UserStore userStore,
  26. RoleManager roleManager,
  27. IRepository<Tenant> tenantRepository,
  28. IMultiTenancyConfig multiTenancyConfig,
  29. IPermissionManager permissionManager,
  30. IUnitOfWorkManager unitOfWorkManager,
  31. ISettingManager settingManager,
  32. IUserManagementConfig userManagementConfig,
  33. IIocResolver iocResolver,
  34. ICacheManager cacheManager,
  35. IRepository<OrganizationUnit, long> organizationUnitRepository,
  36. IRepository<UserOrganizationUnit, long> userOrganizationUnitRepository,
  37. IOrganizationUnitSettings organizationUnitSettings)
  38. : base(
  39. userStore,
  40. roleManager,
  41. tenantRepository,
  42. multiTenancyConfig,
  43. permissionManager,
  44. unitOfWorkManager,
  45. settingManager,
  46. userManagementConfig,
  47. iocResolver,
  48. cacheManager,
  49. organizationUnitRepository,
  50. userOrganizationUnitRepository,
  51. organizationUnitSettings)
  52. {
  53. }
  54. }

别担心依赖列表。他们可能会在下一个版本改变。如果需要的话只是组织构造函数。角色存储和角色管理也类似:

  1. public class RoleStore : AbpRoleStore<Tenant, Role, User>
  2. {
  3. public RoleStore(
  4. IRepository<Role> roleRepository,
  5. IRepository<UserRole, long> userRoleRepository,
  6. IRepository<RolePermissionSetting, long> rolePermissionSettingRepository)
  7. : base(
  8. roleRepository,
  9. userRoleRepository,
  10. rolePermissionSettingRepository
  11. )
  12. {
  13. }
  14. }
  15. public class RoleManager : AbpRoleManager<Tenant, Role, User>
  16. {
  17. public RoleManager(
  18. RoleStore store,
  19. IPermissionManager permissionManager,
  20. IRoleManagementConfig roleManagementConfig,
  21. ICacheManager cacheManager)
  22. : base(
  23. store,
  24. permissionManager,
  25. roleManagementConfig,
  26. cacheManager)
  27. {
  28. }
  29. }

最后,我们要创建一个类租户管理类(这里没有租户存储):

  1. public class TenantManager : AbpTenantManager<Tenant, Role, User>
  2. {
  3. public TenantManager(
  4. IRepository<Tenant> tenantRepository,
  5. IRepository<TenantFeatureSetting, long> tenantFeatureRepository,
  6. EditionManager editionManager) :
  7. base(
  8. tenantRepository,
  9. tenantFeatureRepository,
  10. editionManager
  11. )
  12. {
  13. }
  14. }

最后是功能值存储类以及版本管理类:

  1. public class FeatureValueStore : AbpFeatureValueStore<Tenant, Role, User>
  2. {
  3. public FeatureValueStore(TenantManager tenantManager)
  4. : base(tenantManager)
  5. {
  6. }
  7. }
  8. public class EditionManager : AbpEditionManager
  9. {
  10. public const string DefaultEditionName = "Standard";
  11. public EditionManager(
  12. IRepository<Edition> editionRepository,
  13. IRepository<EditionFeatureSetting, long> editionFeatureRepository)
  14. : base(
  15. editionRepository,
  16. editionFeatureRepository
  17. )
  18. {
  19. }
  20. }

1.2.6 权限检查器

为了使授权系统工作,我们应该实现权限检查器:

  1. public class PermissionChecker : PermissionChecker<Tenant, Role, User>
  2. {
  3. public PermissionChecker(UserManager userManager)
  4. : base(userManager)
  5. {
  6. }
  7. }

1.2.7 基础设施层

1. Entity Framework

如果您选择Entity Framework,我们应该配置它以便使用module-zero。安装Abp.Zero.EntityFramework的NuGet包到EntityFramework项目。然后进入到模块文件(此示例中为AbpZeroSampleDataModule)和改变AbpEntityFrameworkModule依赖到AbpZeroEntityFrameworkModule如下图所示:

  1. [DependsOn(typeof(AbpZeroEntityFrameworkModule), typeof(AbpZeroSampleCoreModule))]
  2. public class AbpZeroSampleDataModule : AbpModule
  3. {
  4. //...
  5. }

2. DbContext

转到您的DbContext类,并改变基类AbpDbContext为AbpZeroDbContext;如下所示:

  1. public class AbpZeroSampleDbContext : AbpZeroDbContext<Tenant, Role, User>
  2. {
  3. //...
  4. }

从而,module-zero里的基础实体被添加到你的数据库环境中。

3. 数据库迁移

现在,我们应该创建数据库迁移,因为我们的数据库上下文被更改了。打开包管理器控制台,然后键入以下命令:

  1. Add-Migration "AbpZero_Installed"

当然,你可以选择不同的迁移名称。不要忘了在包管理器控制台中选择默认工程为AbpZeroSample.EntityFramework(对于你的例子AbpZeroSample将是不同的)。执行这个命令之后,一个新的迁移文件被添加到工程中。检查它,如果你需要可以改变它。然后键入以下命令来更新数据库模式:

  1. Update-Database

您可以检查EntityFramework的代码优先迁移文档以获取更多信息。

4. 初始化数据

如果你检查你的数据库,你会看到表已经被创建,但它们是空的。您可以使用EntityFramework的播种以填补初始数据。您可以使用这样的类作为初始数据生成器:

  1. public class DefaultTenantRoleAndUserBuilder
  2. {
  3. private readonly AbpZeroSampleDbContext _context;
  4. public DefaultTenantRoleAndUserBuilder(AbpZeroSampleDbContext context)
  5. {
  6. _context = context;
  7. }
  8. public void Build()
  9. {
  10. CreateUserAndRoles();
  11. }
  12. private void CreateUserAndRoles()
  13. {
  14. //Admin role for tenancy owner
  15. var adminRoleForTenancyOwner = _context.Roles.FirstOrDefault(r => r.TenantId == null && r.Name == "Admin");
  16. if (adminRoleForTenancyOwner == null)
  17. {
  18. adminRoleForTenancyOwner = _context.Roles.Add(new Role {Name = "Admin", DisplayName = "Admin"});
  19. _context.SaveChanges();
  20. }
  21. //Admin user for tenancy owner
  22. var adminUserForTenancyOwner = _context.Users.FirstOrDefault(u => u.TenantId == null && u.UserName == "admin");
  23. if (adminUserForTenancyOwner == null)
  24. {
  25. adminUserForTenancyOwner = _context.Users.Add(
  26. new User
  27. {
  28. TenantId = null,
  29. UserName = "admin",
  30. Name = "System",
  31. Surname = "Administrator",
  32. EmailAddress = "admin@aspnetboilerplate.com",
  33. IsEmailConfirmed = true,
  34. Password = "AM4OLBpptxBYmM79lGOX9egzZk3vIQU3d/gFCJzaBjAPXzYIK3tQ2N7X4fcrHtElTw==" //123qwe
  35. });
  36. _context.SaveChanges();
  37. _context.UserRoles.Add(new UserRole(adminUserForTenancyOwner.Id, adminRoleForTenancyOwner.Id));
  38. _context.SaveChanges();
  39. }
  40. //Default tenant
  41. var defaultTenant = _context.Tenants.FirstOrDefault(t => t.TenancyName == "Default");
  42. if (defaultTenant == null)
  43. {
  44. defaultTenant = _context.Tenants.Add(new Tenant {TenancyName = "Default", Name = "Default"});
  45. _context.SaveChanges();
  46. }
  47. //Admin role for 'Default' tenant
  48. var adminRoleForDefaultTenant = _context.Roles.FirstOrDefault(r => r.TenantId == defaultTenant.Id && r.Name == "Admin");
  49. if (adminRoleForDefaultTenant == null)
  50. {
  51. adminRoleForDefaultTenant = _context.Roles.Add(new Role { TenantId = defaultTenant.Id, Name = "Admin", DisplayName = "Admin" });
  52. _context.SaveChanges();
  53. }
  54. //Admin for 'Default' tenant
  55. var adminUserForDefaultTenant = _context.Users.FirstOrDefault(u => u.TenantId == defaultTenant.Id && u.UserName == "admin");
  56. if (adminUserForDefaultTenant == null)
  57. {
  58. adminUserForDefaultTenant = _context.Users.Add(
  59. new User
  60. {
  61. TenantId = defaultTenant.Id,
  62. UserName = "admin",
  63. Name = "System",
  64. Surname = "Administrator",
  65. EmailAddress = "admin@aspnetboilerplate.com",
  66. IsEmailConfirmed = true,
  67. Password = "AM4OLBpptxBYmM79lGOX9egzZk3vIQU3d/gFCJzaBjAPXzYIK3tQ2N7X4fcrHtElTw==" //123qwe
  68. });
  69. _context.SaveChanges();
  70. _context.UserRoles.Add(new UserRole(adminUserForDefaultTenant.Id, adminRoleForDefaultTenant.Id));
  71. _context.SaveChanges();
  72. }
  73. }
  74. }

该类创建默认租户,角色和用户。我们可以用它在EF的配置类中初始化我们的数据库数据:

  1. internal sealed class Configuration : DbMigrationsConfiguration<AbpZeroSample.EntityFramework.AbpZeroSampleDbContext>
  2. {
  3. public Configuration()
  4. {
  5. AutomaticMigrationsEnabled = false;
  6. ContextKey = "AbpZeroSample";
  7. }
  8. protected override void Seed(AbpZeroSample.EntityFramework.AbpZeroSampleDbContext context)
  9. {
  10. context.DisableAllFilters();
  11. new DefaultTenantRoleAndUserBuilder(context).Build();
  12. }
  13. }

在这里,我们禁用数据过滤器(所以我们可以自由地操纵数据库),并使用的初始数据生成器类。

1.2.8 表示层

1. NuGet包

添加以下的NuGet包到.Web工程:

  • Abp.Zero.EntityFramework(这也将增加Abp.Zero和所有的依赖)

  • Microsoft.AspNet.Identity.Owin

  • Microsoft.Owin.Host.SystemWeb

2. Owin启动类

添加这样一个Owin启动类:

  1. using AbpZeroSample.Web;
  2. using Microsoft.AspNet.Identity;
  3. using Microsoft.Owin;
  4. using Microsoft.Owin.Security.Cookies;
  5. using Owin;
  6. [assembly: OwinStartup(typeof(Startup))]
  7. namespace AbpZeroSample.Web
  8. {
  9. public class Startup
  10. {
  11. public void Configuration(IAppBuilder app)
  12. {
  13. //对当前应用开启cookie功能,使用它来存储已登录的用户信息
  14. app.UseCookieAuthentication(new CookieAuthenticationOptions
  15. {
  16. AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
  17. LoginPath = new PathString("/Account/Login")
  18. });
  19. //使用cookie来临时性的存储一个已经登录的用户信息,该登录配置的是使用第三方登录提供器
  20. app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
  21. }
  22. }
  23. }

3. 账户控制器

我们可以创建一个控制器用于登录/注销,如下所示:

  1. public class AccountController : AbpZeroSampleControllerBase
  2. {
  3. private readonly UserManager _userManager;
  4. private IAuthenticationManager AuthenticationManager
  5. {
  6. get
  7. {
  8. return HttpContext.GetOwinContext().Authentication;
  9. }
  10. }
  11. public AccountController(UserManager userManager)
  12. {
  13. _userManager = userManager;
  14. }
  15. public ActionResult Login(string returnUrl = "")
  16. {
  17. if (string.IsNullOrWhiteSpace(returnUrl))
  18. {
  19. returnUrl = Request.ApplicationPath;
  20. }
  21. ViewBag.ReturnUrl = returnUrl;
  22. return View();
  23. }
  24. [HttpPost]
  25. public async Task<JsonResult> Login(LoginViewModel loginModel, string returnUrl = "")
  26. {
  27. if (!ModelState.IsValid)
  28. {
  29. throw new UserFriendlyException("Your form is invalid!");
  30. }
  31. var loginResult = await _userManager.LoginAsync(
  32. loginModel.UsernameOrEmailAddress,
  33. loginModel.Password,
  34. loginModel.TenancyName
  35. );
  36. switch (loginResult.Result)
  37. {
  38. case AbpLoginResultType.Success:
  39. break;
  40. case AbpLoginResultType.InvalidUserNameOrEmailAddress:
  41. case AbpLoginResultType.InvalidPassword:
  42. throw new UserFriendlyException("Invalid user name or password!");
  43. case AbpLoginResultType.InvalidTenancyName:
  44. throw new UserFriendlyException("No tenant with name: " + loginModel.TenancyName);
  45. case AbpLoginResultType.TenantIsNotActive:
  46. throw new UserFriendlyException("Tenant is not active: " + loginModel.TenancyName);
  47. case AbpLoginResultType.UserIsNotActive:
  48. throw new UserFriendlyException("User is not active: " + loginModel.UsernameOrEmailAddress);
  49. case AbpLoginResultType.UserEmailIsNotConfirmed:
  50. throw new UserFriendlyException("Your email address is not confirmed!");
  51. default: //Can not fall to default for now. But other result types can be added in the future and we may forget to handle it
  52. throw new UserFriendlyException("Unknown problem with login: " + loginResult.Result);
  53. }
  54. AuthenticationManager.SignOut(DefaultAuthenticationTypes.ExternalCookie);
  55. AuthenticationManager.SignIn(new AuthenticationProperties { IsPersistent = loginModel.RememberMe }, loginResult.Identity);
  56. if (string.IsNullOrWhiteSpace(returnUrl))
  57. {
  58. returnUrl = Request.ApplicationPath;
  59. }
  60. return Json(new MvcAjaxResponse { TargetUrl = returnUrl });
  61. }
  62. public ActionResult Logout()
  63. {
  64. AuthenticationManager.SignOut();
  65. return RedirectToAction("Login");
  66. }
  67. }

附一个简单的LoginViewModel:

  1. public class LoginViewModel
  2. {
  3. public string TenancyName { get; set; }
  4. [Required]
  5. public string UsernameOrEmailAddress { get; set; }
  6. [Required]
  7. public string Password { get; set; }
  8. public bool RememberMe { get; set; }
  9. }

1.2.9 登录视图

为了能够使用的AccountController,我们应该创建一个登录页面。这由你决定创建一个登录表单。只需通过AJAX使用适当的参数调用AccountController.Login。

1. 确保应用程序安全

现在,我们可以添加一个AbpAuthorize特性到HomeController,以确保只有合法的用户才能进入到页面:

  1. [AbpMvcAuthorize]
  2. public class HomeController : AbpZeroSampleControllerBase
  3. {
  4. public ActionResult Index()
  5. {
  6. return View("~/App/Main/views/layout/layout.cshtml"); //Layout of the angular application.
  7. }