這里需要抱歉的是,這里使用的博客園的Markdown語法,代碼顯示不是很好看,沒有行號,而且外面還有一個雙層框,大家將就着看吧!至於這個雙層框,我也不知道怎么回事,不過博客園官方給的例子是沒雙層框的,如果你知道如何去掉這個框,也麻煩回復一下吧!
使用模板創建(自動方式)##
使用ABP和Module-Zero開始一個新的項目最簡單的方式通過ABP官網的模板頁面創建一個解決方案的模板。看下一篇博客《啟動模板》。
手動安裝
如果你之前已經創建了應用,且以后安裝module-zero,那么你可以按下面的步驟來做。
在這篇博客中,我假設你的解決方案有了以下這些項目:
AbpZeroSample.Core
AbpZeroSample.Application
AbpZeroSample.EntityFramework
AbpZeroSample.Web
AbpZeroSample.WebApi
核心(領域)層
將Abp.Zero nuget包安裝到.Core項目中,然后在core module類(本例是AbpZeroSampleCoreModule類)中給AbpZeroCoreModule添加DependsOn特性,如下所示:
[DependsOn(typeof(AbpZeroCoreModule))]
public class AbpZeroSampleCoreModule : AbpModule
{
public override void Initialize()
{
IocManager.RegisterAssemblyByConvention(Assembly.GetExecutingAssembly());
}
}
領域類(實體)
Module-zero提供了抽象類User, Role和Tenant,因此我們應該像下面那樣實現它們:
public class User : AbpUser<Tenant, User>
{
}
public class Role : AbpRole<Tenant, User>
{
}
public class Tenant : AbpTenant<Tenant, User>
{
}
你可以在這里添加自定義的屬性。通過這種方式,我們可以根據自己的需求擴展User, Role和Tenant基類。
管理者(領域服務)
因為管理者和存儲基類都是抽象的,所以我們應該實現它們。
讓我們從User存儲和User管理者開始:
public class UserStore : AbpUserStore<Tenant, Role, User>
{
public UserStore(
IRepository<User, long> userRepository,
IRepository<UserLogin, long> userLoginRepository,
IRepository<UserRole, long> userRoleRepository,
IRepository<Role> roleRepository,
IRepository<UserPermissionSetting, long> userPermissionSettingRepository,
IUnitOfWorkManager unitOfWorkManager,
ICacheManager cacheManager)
: base(
userRepository,
userLoginRepository,
userRoleRepository,
roleRepository,
userPermissionSettingRepository,
unitOfWorkManager,
cacheManager)
{
}
}
public class UserManager : AbpUserManager<Tenant, Role, User>
{
public UserManager(
UserStore userStore,
RoleManager roleManager,
IRepository<Tenant> tenantRepository,
IMultiTenancyConfig multiTenancyConfig,
IPermissionManager permissionManager,
IUnitOfWorkManager unitOfWorkManager,
ISettingManager settingManager,
IUserManagementConfig userManagementConfig,
IIocResolver iocResolver,
ICacheManager cacheManager
)
: base(
userStore,
roleManager,
tenantRepository,
multiTenancyConfig,
permissionManager,
unitOfWorkManager,
settingManager,
userManagementConfig,
iocResolver,
cacheManager)
{
}
}
不要擔心依賴列表,這可能會在下個版本中改變。如果需要的話,只要排列好構造函數就可以了。這對於RoleStore和RoleManager是相似的。
public class RoleStore : AbpRoleStore<Tenant, Role, User>
{
public RoleStore(
IRepository<Role> roleRepository,
IRepository<UserRole, long> userRoleRepository,
IRepository<RolePermissionSetting, long> rolePermissionSettingRepository,
ICacheManager cacheManager)
: base(
roleRepository,
userRoleRepository,
rolePermissionSettingRepository,
cacheManager)
{
}
}
public class RoleManager : AbpRoleManager<Tenant, Role, User>
{
public RoleManager(
RoleStore store,
IPermissionManager permissionManager,
IRoleManagementConfig roleManagementConfig,
ICacheManager cacheManager)
: base(
store,
permissionManager,
roleManagementConfig,
cacheManager)
{
}
}
這里是租戶管理者(tenant manager),沒有租戶存儲類。
public class TenantManager : AbpTenantManager<Tenant, Role, User>
{
public TenantManager(EditionManager editionManager) :
base(editionManager)
{
}
}
最后是特征值存儲和版本管理者。
public class FeatureValueStore : AbpFeatureValueStore<Tenant, Role, User>
{
public FeatureValueStore(TenantManager tenantManager)
: base(tenantManager)
{
}
}
public class EditionManager : AbpEditionManager
{
}
權限管理者
為了使授權系統生效,我們應該實現權限管理者:
public class PermissionChecker : PermissionChecker<Tenant, Role, User>
{
public PermissionChecker(UserManager userManager)
: base(userManager)
{
}
}
基礎設施層
EntityFramework
如果你選擇了EntityFramework,那么我們應該給它配置module-zero。將Abp.Zero.EntityFramework nuget包安裝到.EntityFramework項目中,然后在module文件(本例中是AbpZeroSampleDataModule)中,將AbpEntityFrameworkModule依賴改為AbpZeroEntityFrameworkModule,如下所示:
[DependsOn(typeof(AbpZeroEntityFrameworkModule), typeof(AbpZeroSampleCoreModule))]
public class AbpZeroSampleDataModule : AbpModule
{
//...
}
DbContext
在DbContext類中,將基類從AbpDbContext改為AbpZeroDbContext,如下所示:
public class AbpZeroSampleDbContext : AbpZeroDbContext<Tenant, Role, User>
{
//...
}
這樣,來自module-zero的實體基類就加到了你的數據庫上下文中。
數據庫遷移(Database Migration)
因為我們的數據庫上下文已經改變了,所以,現在我們應該創建數據庫遷移。打開包管理器控制台,然后輸入以下命令:
Add-Migration "AbpZero_Installed"
當然,你可以選擇一個不同的遷移名字。在包管理器控制台管理器中不要忘了選擇默認的項目為.EntityFramework項目。執行完此命令后,項目中會生成一個新的遷移文件。檢查一下,如果需要的話,你可以做出修改。然后輸入下面的命令來更新數據庫模式:
Update-Database
你可以查看EF Code-First文檔,獲取更多信息。
初始化數據(Initial Data)
如果你檢查了你的數據庫,那么你會發現數據表已經創建了,但是表都是空的。你可以通過EF的種子來初始化數據。你可以使用這么一個類作為初始化數據生成器:
public class DefaultTenantRoleAndUserBuilder
{
private readonly AbpZeroSampleDbContext _context;
public DefaultTenantRoleAndUserBuilder(AbpZeroSampleDbContext context)
{
_context = context;
}
public void Build()
{
CreateUserAndRoles();
}
private void CreateUserAndRoles()
{
//Admin role for tenancy owner
var adminRoleForTenancyOwner = _context.Roles.FirstOrDefault(r => r.TenantId == null && r.Name == "Admin");
if (adminRoleForTenancyOwner == null)
{
adminRoleForTenancyOwner = _context.Roles.Add(new Role {Name = "Admin", DisplayName = "Admin"});
_context.SaveChanges();
}
//Admin user for tenancy owner
var adminUserForTenancyOwner = _context.Users.FirstOrDefault(u => u.TenantId == null && u.UserName == "admin");
if (adminUserForTenancyOwner == null)
{
adminUserForTenancyOwner = _context.Users.Add(
new User
{
TenantId = null,
UserName = "admin",
Name = "System",
Surname = "Administrator",
EmailAddress = "admin@aspnetboilerplate.com",
IsEmailConfirmed = true,
Password = "AM4OLBpptxBYmM79lGOX9egzZk3vIQU3d/gFCJzaBjAPXzYIK3tQ2N7X4fcrHtElTw==" //123qwe
});
_context.SaveChanges();
_context.UserRoles.Add(new UserRole(adminUserForTenancyOwner.Id, adminRoleForTenancyOwner.Id));
_context.SaveChanges();
}
//Default tenant
var defaultTenant = _context.Tenants.FirstOrDefault(t => t.TenancyName == "Default");
if (defaultTenant == null)
{
defaultTenant = _context.Tenants.Add(new Tenant {TenancyName = "Default", Name = "Default"});
_context.SaveChanges();
}
//Admin role for 'Default' tenant
var adminRoleForDefaultTenant = _context.Roles.FirstOrDefault(r => r.TenantId == defaultTenant.Id && r.Name == "Admin");
if (adminRoleForDefaultTenant == null)
{
adminRoleForDefaultTenant = _context.Roles.Add(new Role { TenantId = defaultTenant.Id, Name = "Admin", DisplayName = "Admin" });
_context.SaveChanges();
}
//Admin for 'Default' tenant
var adminUserForDefaultTenant = _context.Users.FirstOrDefault(u => u.TenantId == defaultTenant.Id && u.UserName == "admin");
if (adminUserForDefaultTenant == null)
{
adminUserForDefaultTenant = _context.Users.Add(
new User
{
TenantId = defaultTenant.Id,
UserName = "admin",
Name = "System",
Surname = "Administrator",
EmailAddress = "admin@aspnetboilerplate.com",
IsEmailConfirmed = true,
Password = "AM4OLBpptxBYmM79lGOX9egzZk3vIQU3d/gFCJzaBjAPXzYIK3tQ2N7X4fcrHtElTw==" //123qwe
});
_context.SaveChanges();
_context.UserRoles.Add(new UserRole(adminUserForDefaultTenant.Id, adminRoleForDefaultTenant.Id));
_context.SaveChanges();
}
}
}
這個類會創建默認的租戶,角色和用戶。我們可以在EF的Configuration類中將種子數據初始化到數據庫:
internal sealed class Configuration : DbMigrationsConfiguration<AbpZeroSample.EntityFramework.AbpZeroSampleDbContext>
{
public Configuration()
{
AutomaticMigrationsEnabled = false;
ContextKey = "AbpZeroSample";
}
protected override void Seed(AbpZeroSample.EntityFramework.AbpZeroSampleDbContext context)
{
context.DisableAllFilters();
new DefaultTenantRoleAndUserBuilder(context).Build();
}
}
這里,我們禁用了數據過濾器(為的是我們可以自由地操作數據庫)並使用了初始化數據生成器類。
展示層
Nuget包
將下面的nuget包添加到.Web項目中:
- Abp.Zero.EntityFramework(這個也會添加Abp.Zero和所有依賴)
- Microsoft.AspNet.Identity.Owin
- Microsoft.Owin.Host.SystemWeb
Owin Startup類
添加一個Owin Startup類如下:
using AbpZeroSample.Web;
using Microsoft.AspNet.Identity;
using Microsoft.Owin;
using Microsoft.Owin.Security.Cookies;
using Owin;
[assembly: OwinStartup(typeof(Startup))]
namespace AbpZeroSample.Web
{
public class Startup
{
public void Configuration(IAppBuilder app)
{
// Enable the application to use a cookie to store information for the signed in user
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/Account/Login")
});
// Use a cookie to temporarily store information about a user logging in with a third party login provider
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
}
}
}
Account Controller
我們可以創建一個用於登錄/注銷的控制器,如下:
public class AccountController : AbpZeroSampleControllerBase
{
private readonly UserManager _userManager;
private IAuthenticationManager AuthenticationManager
{
get
{
return HttpContext.GetOwinContext().Authentication;
}
}
public AccountController(UserManager userManager)
{
_userManager = userManager;
}
public ActionResult Login(string returnUrl = "")
{
if (string.IsNullOrWhiteSpace(returnUrl))
{
returnUrl = Request.ApplicationPath;
}
ViewBag.ReturnUrl = returnUrl;
return View();
}
[HttpPost]
public async Task<JsonResult> Login(LoginViewModel loginModel, string returnUrl = "")
{
if (!ModelState.IsValid)
{
throw new UserFriendlyException("Your form is invalid!");
}
var loginResult = await _userManager.LoginAsync(
loginModel.UsernameOrEmailAddress,
loginModel.Password,
loginModel.TenancyName
);
switch (loginResult.Result)
{
case AbpLoginResultType.Success:
break;
case AbpLoginResultType.InvalidUserNameOrEmailAddress:
case AbpLoginResultType.InvalidPassword:
throw new UserFriendlyException("Invalid user name or password!");
case AbpLoginResultType.InvalidTenancyName:
throw new UserFriendlyException("No tenant with name: " + loginModel.TenancyName);
case AbpLoginResultType.TenantIsNotActive:
throw new UserFriendlyException("Tenant is not active: " + loginModel.TenancyName);
case AbpLoginResultType.UserIsNotActive:
throw new UserFriendlyException("User is not active: " + loginModel.UsernameOrEmailAddress);
case AbpLoginResultType.UserEmailIsNotConfirmed:
throw new UserFriendlyException("Your email address is not confirmed!");
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
throw new UserFriendlyException("Unknown problem with login: " + loginResult.Result);
}
AuthenticationManager.SignOut(DefaultAuthenticationTypes.ExternalCookie);
AuthenticationManager.SignIn(new AuthenticationProperties { IsPersistent = loginModel.RememberMe }, loginResult.Identity);
if (string.IsNullOrWhiteSpace(returnUrl))
{
returnUrl = Request.ApplicationPath;
}
return Json(new MvcAjaxResponse { TargetUrl = returnUrl });
}
public ActionResult Logout()
{
AuthenticationManager.SignOut();
return RedirectToAction("Login");
}
}
還有一個簡單的LoginViewModel類:
public class LoginViewModel
{
public string TenancyName { get; set; }
[Required]
public string UsernameOrEmailAddress { get; set; }
[Required]
public string Password { get; set; }
public bool RememberMe { get; set; }
}
Login視圖
為了可以使用AccountController,我們應該創建一個login頁面。創建一個login表單取決於你。只需要經由具有合適參數的AJAX調用AccountController的Login方法就可以了。
應用安全
現在,我們可以在HomeController上添加一個AbpAuthorize特性就可以確保只有已經驗證的用戶可以進入該頁面:
[AbpMvcAuthorize]
public class HomeController : AbpZeroSampleControllerBase
{
public ActionResult Index()
{
return View("~/App/Main/views/layout/layout.cshtml"); //Layout of the angular application.
}
}