用戶實體##
用戶實體代表應用的一個用戶,它派生自AbpUser類,如下所示:
public class User : AbpUser<Tenant, User>
{
//這里添加你自己的用戶屬性
}
這個類是你在安裝module-zero時自動創建的。用戶數據存儲在數據庫中的AbpUsers表。你可以添加User類的自定義屬性(以及針對改變創建數據庫遷移)。
AbpUser類定義的基本屬性如下:
- UserName:用戶的登錄名,對於一個租戶來說應該是唯一的。
- EmailAddress:用戶的郵箱地址。對於租戶來說應該是唯一的。
- Password:用戶的哈希密碼。
- IsActive:如果用戶可以登錄到該應用,那么此值為true。
- Name和Surname:用戶的名和姓。
還有許多屬性,如Roles, Permissions, Tenant, Settings, IsEmailConfirmed等等。你可以在AbpUser類中查看更多信息。
AbpUser類派生自FullAuditedEntity。這意味着它有創建,修改和刪除的審計屬性。它也支持軟刪除。因此當我們刪除一個用戶的時候,實際上它並沒有從數據庫中刪除,而是僅僅標記為已刪除的狀態。
為了在一個多租戶的應用中更好地工作,AbpUser類實現了IMayHaveTenant過濾器。
最后,User的Id定義為long類型。
用戶管理者##
用戶管理者是執行用戶領域邏輯的服務:
public class UserManager : AbpUserManager<Tenant, Role, User>
{
//...
}
你可以注入用戶管理者,然后使用它來創建,刪除,更新用戶,為用戶授權,改變角色以及更多。你可以在這里添加你自己的方法。而且,你可以重寫AbpUserManager基類的任何方法來滿足你自己的需求。
多租戶
如果你創建的不是一個多租戶應用,那么你可以跳過本節。
曾經設計UserManager的目的是為單租戶服務的。默認是為當前租戶服務的。接下來看一下UserManager的一些用法:
public class MyTestAppService : ApplicationService
{
private readonly UserManager _userManager;
public MyTestAppService(UserManager userManager)
{
_userManager = userManager;
}
public void TestMethod_1()
{
//Find a user by email for current tenant
var user = _userManager.FindByEmail("sampleuser@aspnetboilerplate.com");
}
public void TestMethod_2()
{
//Switch to tenant 42
CurrentUnitOfWork.SetFilterParameter(AbpDataFilters.MayHaveTenant, AbpDataFilters.Parameters.TenantId, 42);
//Find a user by email for the tenant 42
var user = _userManager.FindByEmail("sampleuser@aspnetboilerplate.com");
}
public void TestMethod_3()
{
//Disabling MayHaveTenant filter, so we can reach to all users
using (CurrentUnitOfWork.DisableFilter(AbpDataFilters.MayHaveTenant))
{
//Now, we can search for a user name in all tenants
var users = _userManager.Users.Where(u => u.UserName == "sampleuser").ToList();
//Or we can add TenantId filter if we want to search for a specific tenant
var user = _userManager.Users.FirstOrDefault(u => u.TenantId == 42 && u.UserName == "sampleuser");
}
}
}
用戶登錄
UserManager有一個登錄到該應用的LoginAsync方法。它檢查所有的登錄邏輯並返回一個登錄結果。查看樣例AccountController中Login方法的示例用法。
關於IdentityResults
UserManager的一些方法返回了IdentityResult作為結果而不是拋出一些情況的異常。這是ASP.NET Identity Framework的本質。Module-zero也遵循這個。因此,我們可以查看這個返回的結果對象就可知道操作是否成功。
Module-zero定義了CheckErrors擴展方法,它可以自動地檢查錯誤,如果需要,也會拋出異常(本地化的UserFriendlyException)。樣例用法:
(await UserManager.CreateAsync(user)).CheckErrors();
為了獲得一個本地化的異常,我們應該提供一個ILocalizationManager實例:
(await UserManager.CreateAsync(user)).CheckErrors(LocalizationManager);
外部認證
Module-zero的Login方法會認證數據庫的AbpUsers表中的用戶。一些應用可能要求認證來自外部資源的用戶(比如活動目錄,來自其他數據庫的表,甚至來自一個遠程服務)。
對於很多情況,UserManager定義了一個名叫“外部認證資源”的擴展點。我們可以創建一個派生自**IExternalAuthenticationSource 的類,然后將它注冊到配置中。有一個簡化了IExternalAuthenticationSource的實現的類DefaultExternalAuthenticationSource **,來看一個例子:
public class MyExternalAuthSource : DefaultExternalAuthenticationSource<Tenant, User>
{
public override string Name
{
get { return "MyCustomSource"; }
}
public override Task<bool> TryAuthenticateAsync(string userNameOrEmailAddress, string plainPassword, Tenant tenant)
{
//TODO: authenticate user and return true or false
}
}
在TryAuthenticateAsync方法中,我們可以檢查來自某些資源的用戶名和密碼,如果給定的用戶通過了該資源的認證,那么返回true。而且,我們可以重寫CreateUser和UpdateUser方法來控制該資源的用戶創建和更新。
當外部資源驗證通過一個用戶后,module-zero會檢查數據庫(AbpUser表)中是否存在該用戶。如果不存在,就會調用CreateUser來創建該用戶,否則調用UpdateUser使外部源更新已存在的用戶信息。
在一個應用中,我們可以定義不止一個外部源。AbpUser實體有一個AuthenticationSource屬性,它表明了哪個源認證了該用戶。
為了注冊認證源,我們可以在模塊中的PreInitialize方法使用這些代碼:
Configuration.Modules.Zero().UserManagement.ExternalAuthenticationSources.Add<MyExternalAuthSource>();
LDAP/活動目錄
LdapAuthenticationSource是一個外部認證的實現,它可以讓用戶使用他們的LDAP(活動目錄)用戶名和密碼登錄。
如果我們想要使用LDAP認證,那么我們首先要將Abp.Zero.Ldap添加到項目中(通常添加到Core(領域)項目)。然后,我們應該給應用擴展LdapAuthenticationSource,如下所示:
public class MyLdapAuthenticationSource : LdapAuthenticationSource<Tenant, User>
{
public MyLdapAuthenticationSource(ILdapSettings settings, IAbpZeroLdapModuleConfig ldapModuleConfig)
: base(settings, ldapModuleConfig)
{
}
}
最后,我們應該設置AbpZeroLdapModule的模塊依賴,然后開啟上面創建的LDAP認證源:
[DependsOn(typeof(AbpZeroLdapModule))]
public class MyApplicationCoreModule : AbpModule
{
public override void PreInitialize()
{
Configuration.Modules.ZeroLdap().Enable(typeof (MyLdapAuthenticationSource));
}
...
}
這些步驟之后,你的應用就開啟了LDAP模塊。但LDAP認證默認沒有開啟,我們可以使用設置來開啟它。
設置
LdapSettingNames類定義了一些設置名稱的常量。當要改變設置(或者獲取設置)時,你可以使用這些常量名稱。LDAP設置是每個租戶的(對於多租戶應用)。因此,不同的租戶有不同的設置。(在github上查看設置的定義)
正如你在MyLdapAuthenticationSource的構造函數中看到的,LdapAuthenticationSource期望ILdapSettings作為構造函數參數。該接口用於獲得LDAP設置,如領域,用戶名和密碼,以連接到活動目錄。默認的實現(LdapSetting類)從設置管理者中獲得這些設置。
如果你使用了設置管理者,你可以使用設置管理者的API改變LDAP的設置。如果你想要的話,你可以通過將一個初始化/種子數據添加到數據庫來默認開啟LDAP認證。
注意:如果你沒有定義領域,用戶名和密碼,且你的應用運行在具有合適權限的領域中,那么LDAP認證只對當前的領域有效。
自定義設置
如果你想定義其他的設置源,那么你可以實現一個自定義的ILdapSettings類,如下所示:
public class MyLdapSettings : ILdapSettings
{
public async Task<bool> GetIsEnabled(int? tenantId)
{
return true;
}
public async Task<ContextType> GetContextType(int? tenantId)
{
return ContextType.Domain;
}
public async Task<string> GetContainer(int? tenantId)
{
return null;
}
public async Task<string> GetDomain(int? tenantId)
{
return null;
}
public async Task<string> GetUserName(int? tenantId)
{
return null;
}
public async Task<string> GetPassword(int? tenantId)
{
return null;
}
}
然后在模塊中的PreInitialize方法里將它注冊到IOC中:
[DependsOn(typeof(AbpZeroLdapModule))]
public class MyApplicationCoreModule : AbpModule
{
public override void PreInitialize()
{
IocManager.Register<ILdapSettings, MyLdapSettings>(); //change default setting source
Configuration.Modules.ZeroLdap().Enable(typeof (MyLdapAuthenticationSource));
}
...
}
這樣,你就可以從其他資源獲得LDAP設置了。