ABP+AdminLTE+Bootstrap Table权限管理系统第八节--ABP错误机制及AbpSession相关
? 返回總目錄:ABP+AdminLTE+Bootstrap Table權(quán)限管理系統(tǒng)一期
? ? ?上一節(jié)我們講到登錄邏輯,我做的登錄邏輯很簡(jiǎn)單的,我們來看一下abp?module-zero里面的登錄代碼.
#region Login / Logoutpublic ActionResult Login(string returnUrl = ""){if (string.IsNullOrWhiteSpace(returnUrl)){returnUrl = Request.ApplicationPath;}return View(new LoginFormViewModel{ReturnUrl = returnUrl,IsMultiTenancyEnabled = _multiTenancyConfig.IsEnabled});}[HttpPost]public async Task<JsonResult> Login(LoginViewModel loginModel, string returnUrl = "", string returnUrlHash = ""){CheckModelState();var loginResult = await GetLoginResultAsync(loginModel.UsernameOrEmailAddress,loginModel.Password,loginModel.TenancyName);await SignInAsync(loginResult.User, loginResult.Identity, loginModel.RememberMe);if (string.IsNullOrWhiteSpace(returnUrl)){returnUrl = Request.ApplicationPath;}if (!string.IsNullOrWhiteSpace(returnUrlHash)){returnUrl = returnUrl + returnUrlHash;}return Json(new AjaxResponse { TargetUrl = returnUrl });}private async Task<AbpLoginResult<Tenant, User>> GetLoginResultAsync(string usernameOrEmailAddress, string password, string tenancyName){try{var loginResult = await _logInManager.LoginAsync(usernameOrEmailAddress, password, tenancyName);switch (loginResult.Result){case AbpLoginResultType.Success:return loginResult;default:throw CreateExceptionForFailedLoginAttempt(loginResult.Result, usernameOrEmailAddress, tenancyName);}}catch (Exception e){Console.WriteLine(e);throw;}}private async Task SignInAsync(User user, ClaimsIdentity identity = null, bool rememberMe = false){if (identity == null){identity = await _userManager.CreateIdentityAsync(user, DefaultAuthenticationTypes.ApplicationCookie);}AuthenticationManager.SignOut(DefaultAuthenticationTypes.ApplicationCookie);AuthenticationManager.SignIn(new AuthenticationProperties { IsPersistent = rememberMe }, identity);}private Exception CreateExceptionForFailedLoginAttempt(AbpLoginResultType result, string usernameOrEmailAddress, string tenancyName){switch (result){case AbpLoginResultType.Success:return new ApplicationException("Don't call this method with a success result!");case AbpLoginResultType.InvalidUserNameOrEmailAddress:case AbpLoginResultType.InvalidPassword:return new UserFriendlyException(L("LoginFailed"), L("InvalidUserNameOrPassword"));case AbpLoginResultType.InvalidTenancyName:return new UserFriendlyException(L("LoginFailed"), L("ThereIsNoTenantDefinedWithName{0}", tenancyName));case AbpLoginResultType.TenantIsNotActive:return new UserFriendlyException(L("LoginFailed"), L("TenantIsNotActive", tenancyName));case AbpLoginResultType.UserIsNotActive:return new UserFriendlyException(L("LoginFailed"), L("UserIsNotActiveAndCanNotLogin", usernameOrEmailAddress));case AbpLoginResultType.UserEmailIsNotConfirmed:return new UserFriendlyException(L("LoginFailed"), "UserEmailIsNotConfirmedAndCanNotLogin");case AbpLoginResultType.LockedOut:return new UserFriendlyException(L("LoginFailed"), L("UserLockedOutMessage"));default: //Can not fall to default actually. But other result types can be added in the future and we may forget to handle itLogger.Warn("Unhandled login fail reason: " + result);return new UserFriendlyException(L("LoginFailed"));}}public ActionResult Logout(){AuthenticationManager.SignOut();return RedirectToAction("Login");}#endregion由于abp涉及到租戶和身份驗(yàn)證的問題,所以登錄有點(diǎn)繁瑣.分析發(fā)現(xiàn)主要包括以下幾個(gè)步驟:
1、 GetLoginResultAsync --> loginManager.LoginAsync --> userManager.CreateIdentityAsync:不要以為調(diào)用了LoginAsync就以為是登錄,其實(shí)這是偽登錄。主要根據(jù)用戶名密碼去核對(duì)用戶信息,構(gòu)造User對(duì)象返回,然后再根據(jù)User對(duì)象的身份信息去構(gòu)造身份證(CliamsIdentity)。2、SignInAsync --> AuthenticationManager.SignOut
-->AuthenticationManager.SignIn :
AuthenticationManager(認(rèn)證管理員),負(fù)責(zé)真正的登入登出。SignIn的時(shí)候?qū)⒌谝徊綐?gòu)造的身份證(CliamsIdentity)交給證件所有者(ClaimsPrincipal)。 ?? 登錄完成之后,我們通常會(huì)有一個(gè)記住用戶名密碼的功能,有人就會(huì)想到abp中的AbpSession.單其實(shí)AbpSession不是單純意義上的Session,比如AbpSession里面的Userid就是通過以下方式獲得的. ((ClaimsPrincipal)Thread.CurrentPrincipal).Claims.FirstOrDefault(c => c.Type == ClaimTypes.NameIdentifier);
? ? ? ?需要獲取會(huì)話信息則必須實(shí)現(xiàn)IAbpSession接口。雖然你可以用自己的方式去實(shí)現(xiàn)它(IAbpSession),但是它在module-zero項(xiàng)目中已經(jīng)有了完整的實(shí)現(xiàn)。IAbpSession包含還有其他信息.
//// 摘要:// Defines some session information that can be useful for applications.public interface IAbpSession{//// 摘要:// TenantId of the impersonator. This is filled if a user with Abp.Runtime.Session.IAbpSession.ImpersonatorUserId// performing actions behalf of the Abp.Runtime.Session.IAbpSession.UserId.int? ImpersonatorTenantId { get; }//// 摘要:// UserId of the impersonator. This is filled if a user is performing actions behalf// of the Abp.Runtime.Session.IAbpSession.UserId.long? ImpersonatorUserId { get; }//// 摘要:// Gets current multi-tenancy side.MultiTenancySides MultiTenancySide { get; }//// 摘要:// Gets current TenantId or null. This TenantId should be the TenantId of the Abp.Runtime.Session.IAbpSession.UserId.// It can be null if given Abp.Runtime.Session.IAbpSession.UserId is a host user// or no user logged in.int? TenantId { get; }//// 摘要:// Gets current UserId or null. It can be null if no user logged in.long? UserId { get; }//// 摘要:// Used to change Abp.Runtime.Session.IAbpSession.TenantId and Abp.Runtime.Session.IAbpSession.UserId// for a limited scope.//// 參數(shù):// tenantId://// userId:IDisposable Use(int? tenantId, long? userId);? ? ? ? ? ? ?
AbpSession定義的一些關(guān)鍵屬性:
1.UserId: 當(dāng)前用戶的標(biāo)識(shí)ID,如果沒有當(dāng)前用戶則為null.如果需要授權(quán)訪問則它不可能為空。
2.TenantId: 當(dāng)前租戶的標(biāo)識(shí)ID,如果沒有當(dāng)前租戶則為null。
3.MultiTenancySide: 可能是Host或Tenant。
? ? ? ? ?UserId和TenantId是可以為null的。當(dāng)然也提供了不為空時(shí)獲取數(shù)據(jù)的 GetUserId()和GetTenantId() 方法 。當(dāng)你確定有當(dāng)前用戶時(shí),你可以使用GetUserId()方法。如果當(dāng)前用戶為空,使用該方法則會(huì)拋出一個(gè)異常。GetTenantId()的使用方式和GetUserId()類似。
? ? ? ? ? ? IAbpSession通常是以屬性注入的方式存在于需要它的類中,不需要獲取會(huì)話信息的類中則不需要它。如果我們使用屬性注入方式,我們可以用?
NullAbpSession.Instance作為默認(rèn)值來初始化它(IAbpSession)
? ? ? ? ? 由于授權(quán)是應(yīng)用層的任務(wù),因此我們應(yīng)該在應(yīng)用層和應(yīng)用層的上一層使用IAbpSession(我們不在領(lǐng)域?qū)邮褂肐AbpSession是很正常的)。
ApplicationService,?AbpController?和?AbpApiController?這3個(gè)基類已經(jīng)注入了AbpSession屬性,因此在Application Service的實(shí)例方法中,能直接使用AbpSession屬性。
? ? ? ? ? ?ABP框架中的AbpSession, 并沒有使用到System.Web.HttpSessionStateBase, 而是自己定義了一個(gè)Abp.Runtime.Session.IAbpSession接口, 并在Zero模塊中通過AspNet.Identity組件實(shí)現(xiàn)了AbpSession對(duì)象的存值、取值。 所以即使Web服務(wù)重啟,也不會(huì)丟失Session狀態(tài)。在我們自己的項(xiàng)目中, Session對(duì)象只有UserId、TenantId、MultiTenancySide這幾個(gè)屬性是不夠用的,可以自己擴(kuò)充了幾個(gè)屬性和方法,使用起來非常方便。
? ? ? 首先我們定義IAbpSession擴(kuò)展類獲取擴(kuò)展屬性,通過擴(kuò)展類,我們不需要做其他額外的更改,即可通過ApplicationService, AbpController 和 AbpApiController 這3個(gè)基類已經(jīng)注入的AbpSession屬性調(diào)用GetUserName()來獲取擴(kuò)展的Name屬性。
?接口代碼:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks;namespace JCmsErp.AbpSessionExtension {public interface IAbpSessionExtension{string UserName { get; }} }實(shí)現(xiàn)代碼:
using Abp.Configuration.Startup; using Abp.MultiTenancy; using Abp.Runtime; using Abp.Runtime.Session; using System; using System.Collections.Generic; using System.Linq; using System.Security.Claims; using System.Text; using System.Threading.Tasks;namespace JCmsErp.AbpSessionExtension {public class AbpSessionExtension : ClaimsAbpSession, IAbpSessionExtension{public AbpSessionExtension(IPrincipalAccessor principalAccessor, IMultiTenancyConfig multiTenancy, ITenantResolver tenantResolver, IAmbientScopeProvider<SessionOverride> sessionOverrideScopeProvider): base(principalAccessor, multiTenancy, tenantResolver, sessionOverrideScopeProvider){}public string UserName => GetUserName(ClaimTypes.Name);private string GetUserName(string claimType){var claimsPrincipal = PrincipalAccessor.Principal;var claim = claimsPrincipal?.Claims.FirstOrDefault(c => c.Type == claimType);if (string.IsNullOrEmpty(claim?.Value))return null;return claim.Value;}} }?
? ? ? ?然后在登錄邏輯中加入以下代碼:
//添加身份信息,以便在AbpSession中使用 identity.AddClaim(new Claim(ClaimTypes.Name, user.UserName));就這樣,我們?cè)贏pplicationService, AbpController 和 AbpApiController任何地方注入IAbpSession,然后AbpSession.Name就能獲取到我們登錄時(shí)候添加的信息.
? ? ? ?二,abp的錯(cuò)誤機(jī)制
? ? ? ? ? 如果登錄過程中出錯(cuò)怎么辦,報(bào)錯(cuò)了ABP怎么反應(yīng),我們來看一下abp的錯(cuò)誤機(jī)制.在web應(yīng)用中,異常通常在MVC Controller actions和Web API Controller actions中處理。當(dāng)異常發(fā)生時(shí),應(yīng)用程序的用戶以某種方式被告知錯(cuò)誤的相關(guān)信息及原因。果錯(cuò)誤在正常的HTTP請(qǐng)求時(shí)發(fā)生,將會(huì)顯示一個(gè)異常頁。如果在AJAX請(qǐng)求中發(fā)生錯(cuò)誤,服務(wù)器發(fā)送錯(cuò)誤信息到客戶端,然后客戶端處理錯(cuò)誤并顯示給用戶。在所有的Web請(qǐng)求中處理異常是件乏味且重復(fù)的工作。ABP自動(dòng)化完成異常處理,幾乎從不需要顯示的處理任何異常。ABP處理所有的異常、記錄異常并返回合適、格式化的響應(yīng)到客戶端。在客戶端處理這些響應(yīng)并將錯(cuò)誤信息顯示給用戶。
? ? ?異常顯示,首先我們?cè)贏ctionResult 隨便添加一個(gè)異常信息,調(diào)試一下看一下結(jié)果
? ?
public ActionResult Index(){// return View();throw new Exception("登錄密碼錯(cuò)誤或用戶不存在或用戶被禁用。");}? ? ? ? ?當(dāng)然,這個(gè)異??赡苡闪硪粋€(gè)方法拋出,而這個(gè)方法的調(diào)用在這個(gè)action里。ABP處理這個(gè)異常、記錄它并顯示'Error.cshtml'視圖。你可以自定義這個(gè)視圖來顯示錯(cuò)誤。一個(gè)示例錯(cuò)誤視圖(在ABP模板中的默認(rèn)錯(cuò)誤視圖):
?
?
BP對(duì)用戶隱藏了異常的細(xì)節(jié)并顯示了一個(gè)標(biāo)準(zhǔn)(本地化的)的錯(cuò)誤信息,除非你顯示的拋出一個(gè)UserFriendlyException,UserFriendlyException UserFriendlyException是一個(gè)特殊類型的異常,它直接顯示給用戶。參見下面的示例:
// GET: Accountpublic ActionResult Index(){// return View();throw new Abp.UI.UserFriendlyException("登錄密碼錯(cuò)誤或用戶不存在或用戶被禁用。");}瀏覽器結(jié)果:
?
?
?
所以,如果你想顯示一個(gè)特定的錯(cuò)誤信息給用戶,那就拋出一個(gè)UserFriedlyException(或者一個(gè)繼承自這個(gè)類的異常)。
? ? ? 當(dāng)然如果是ajax請(qǐng)求里面出錯(cuò),message API處理JSON對(duì)象并顯示錯(cuò)誤信息給用戶。前端應(yīng)該有相應(yīng)的錯(cuò)誤處理.
??返回總目錄:ABP+AdminLTE+Bootstrap Table權(quán)限管理系統(tǒng)一期
轉(zhuǎn)載于:https://www.cnblogs.com/anyushengcms/p/7261815.html
總結(jié)
以上是生活随笔為你收集整理的ABP+AdminLTE+Bootstrap Table权限管理系统第八节--ABP错误机制及AbpSession相关的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 梦到死了是什么意思
- 下一篇: P2327 [SCOI2005]扫雷