• 会话管理之AbpSession


    一.AbpSession的认识

    在ABP中提供了IAbpSession的接口用来获取用户和租户的信息,没有使用Asp.Net中的Session,那么AbpSession到底和Session有没有关系?具体是怎么实现的呢?

    在ABP的源码中共有两个类具体实现了IAbpSession接口:NullAbpSession和ClaimAbpSession,其中NullAbpSession实现了空对象设计模式,那么主要的代码就是在这个ClaimAbpSession中

    从上面的代码中我们可以看出我们的用户ID是通过PrincipalAcessor.Principal获取到的,那么这又是一个什么东西呢?

    其中ClaimsPricipal是微软提供的用于登陆验证的的一个类。从上面的代码中,我们终于了解到其实AbpSession和Session没有任何关系的。我们的AbpSession就是从这个类里面获取的相应的信息(用户ID,租户ID,模拟用户Id,模拟租户ID),那么问题来了,这个ClaimsPricipal到底又是一个什么东西呢?

    二.了解Identity

    1.为什么要使用Asp.net Identity,它有什么优势呢?

    1).作为一种用户角色管理的组件,支持Asp.net MVC、Asp.net Core 、WebApi等微软的框架,可以很方便的集成。其实我们新建的MVC项目中,已经把Identity的相关组件库引用进去了。

    2).支持持久化,可以很方便的对接EF和EFCore

    3).可以很容易的创建角色和用户,管理方便

    4).可以很方方便的和第三方用户登陆对接。

    2.identity中有三个比较重要的对象。

    Claim(证件信息,重点是信息)

    简单来说这个对象就是一个键值对,存放信息用的

    上面的type表示的信息的类型,在identity中其实已经提供了一些经常使用的类型的类ClaimTypes

    ClaimIdentity(证件)

    这个东西存放的就是我们Claim的集合,通过很多的Claim信息,我们就可以拼装成我们的证件ClaimIdentity,比如我们的身份证,需要提供性别,姓名,出生年月,家庭地址等Claim信息,然后就组装成了我们的身份证(ClaimIdentity)

     其中AuthenticationType就是证件的类型,我们的证件信息组成的证件可以存在多种展现形式,拿我们的身份证来说,有实体证件和电子复印件。还有我们的车票,可以是电子票,也可以是实体票

    ClaimsPrincipal(证件持有者)

    这个类中存放了很多的证件ClaimIdentity,它就像一个人,同时可以持有身份证,车票,三好学生证等证件

    3.Asp.net Identity的是实例应用

    其实在我们创建我们的MVC项目的时候,在菜单栏中就存在相应的选项,是否使用身份验证。

    接下来我将通过AccountController(账号登陆注册控制器)详细了解下一Identity的机制,在Identity中存在三个重要的Manager对象

    1.UserMananger

    UserMananger对象用来处理创建用户等逻辑

    2.SignManager

    SignManager对象用来对用户的登陆和登出的逻辑进行处理

    3.RoleManager

    RoleManager对象用来对用用户角色相关逻辑进行处理。

     三.Abp中Claim的使用

    从上面的代码中,我们可以看出在_logInManager.LoginAsync这个方法中,会将我们的Claim信息构造为证件ClaimIdentity,这个可以在Abp源码中看到。那么我们的ClaimIdentiy这些证件信息是如何添加到证件持有人ClaimPricipal中的呢?只有将信息存放在ClaimPricipal中,才会有最上面,从ClaimPricipal中获取我们的用户信息的操作。其实我们猜一猜可以知道。这个操作肯定是在登陆的时候,信息存放在ClaimPricipal中的。其实_logInManager.LoginAsync也实现了。但是通过源码中我们发现,不仅仅这个方法中实现了往ClaimPricipal中添加信息的,在后面的SignManager.SignAsync中也实现了,这个将是我们后面扩展AbpSession的关键,具体的源码将在AbpSession扩展中给出。

    四.AbpSession的扩展

    我们已经知道AbpSession并不是Session,我们的登陆信息没有使用Session的方式存储,而是使用ClaimPrincipal的方式存放的,那么我们需要额外的存放一些我们的信息在我们的Abp系统中,需要怎么做呢?其实很简单啊,不就是我向ClaimPricipal中的添加ClaiIdentity嘛。首先看一下我们Account中代码

    答案是可以的,这个我们可以从Abp的源码中找到答案。

     

    在扩展前,我们需要将我们自定义的Claim信息添加到ClaimIdentity中去,修改AccountController的Login方法

    第一种扩展思路:通过对AbpSession添加扩展方法的方式,获取到自定义的Claim

    在Core层添加我们的扩展类
     public static class AbpSessionExtension
        {
     public static string GetName(this IAbpSession session)
            {
                return GetClaimValue(ClaimTypes.Email);
            }
    
            private static string GetClaimValue(string claimType)
            {
              
                 var claimsPrincipal = DefaultPrincipalAccessor.Instance.Principal;
    
                var claim = claimsPrincipal?.Claims.FirstOrDefault(c => c.Type == claimType);
                if (string.IsNullOrEmpty(claim?.Value))
                    return null;
    
                return claim.Value;
            }
        }
    
    在Controller中调用
     public class HomeController : StudyABPProjectControllerBase
        {
          
            public ActionResult Index()
            {
                var name = AbpSession.GetName();
                return Content(name);
            }
        }

    理想是美好的,现实是残酷的,在abp.core项目中,无法获取到我们自定义的信息,因为DefaulPricinpalAccessor.Instance,就是通过Thread.CurentPrincipal获取的当前的证件拥有者,但是在Core项目中,这个对象始终为null,所以导致无法获取到principal。

    第二种扩展思路:直接修改原有AbpSession,使用我们自己定义的AbpSession覆盖原来的,然后在我们自定义的AbpSession中添加我们自定义的属性

    1.首先创建我们的MyAbpSession和IMyAbpSession

      public class MyAbpSession : ClaimsAbpSession, IMyAbpSession
        {
            public MyAbpSession(IPrincipalAccessor principalAccessor, IMultiTenancyConfig multiTenancy, ITenantResolver tenantResolver, IAmbientScopeProvider<SessionOverride> sessionOverrideScopeProvider) : base(principalAccessor, multiTenancy, tenantResolver, sessionOverrideScopeProvider)
            {
            }
    
            private  string GetClaimValue(string claimType)
            {
    
                var claimsPrincipal = this.PrincipalAccessor.Principal;
    
                var claim = claimsPrincipal?.Claims.FirstOrDefault(c => c.Type == claimType);
                if (string.IsNullOrEmpty(claim?.Value))
                    return null;
    
                return claim.Value;
            }
            public string MyName => GetClaimValue("MyName");
        }
    
      public interface IMyAbpSession:IAbpSession
        {
            string MyName { get; }
        }

    2.在UI层和Application层覆盖原来的AbpSession

      public abstract class StudyABPProjectControllerBase: AbpController
        {
            public new IMyAbpSession AbpSession { get; set; }
            protected StudyABPProjectControllerBase()
            {
                LocalizationSourceName = StudyABPProjectConsts.LocalizationSourceName;
            }
    
            protected void CheckErrors(IdentityResult identityResult)
            {
                identityResult.CheckErrors(LocalizationManager);
            }
        }

    直接运行我们代码

     

    OK,我们的AbpSession通过重写原来的AbpSession正确获取到了我们添加的数据。

  • 相关阅读:
    一些想说的事
    化学离子平衡作业偷懒神器
    solution
    SGU 刷题记
    INT128
    # 字典树的指针写法 1.
    CSP-S2 游记
    Tarjan 【整理】
    HGOI 20191106
    20191101
  • 原文地址:https://www.cnblogs.com/XZhao/p/9261175.html
Copyright © 2020-2023  润新知