美文网首页ABP
ABP 源码解析 五. Session

ABP 源码解析 五. Session

作者: 诸葛_小亮 | 来源:发表于2018-08-22 21:23 被阅读36次

    介绍

    此系列文章主要是对ABP源码进行解读,初探作者在创造ABP的框架思路,和使用到的设计模式进行。
    通过解读ABP源码,可以提升ABP使用方式,可以提升编码意识,提高面向对象编程思想。

    《ABP 源码解析 四. 启动配置》中介绍了ABP的启动配置功能。

    本篇文章主要介绍ABP框架的Session机制。

    参考文章:


    UML

    UML
    • IAbpSession: 定义一些对应用程序有用的会话信息, 诸如 当前用户id,当前租户id等信息
    • AbpSessionBase : 会话基类,定义虚方法和属性,以便子类重载
    • ClaimsAbpSession:实现从当前Claims中获得会话属性
    • NullAbpSession:实现IAbpSession空对象模式,一般测试使用
    • IPrincipalAccessor:定义 Claims访问器
    • DefaultPrincipalAccessor:默认的Claims访问器

    源码解析

    1. 定义会话接口

    会话接口主要包括当前会话信息,如当前登陆者id,当前登录的 tenantid ,以及提供限定范围内修改登录者id和tenatid信息的方法

    /// <summary>
        /// Defines some session information that can be useful for applications.
        /// 定义一些对应用程序有用的会话信息
        /// </summary>
        public interface IAbpSession
        {
            /// <summary>
            /// Gets current UserId or null.
            /// It can be null if no user logged in.
            /// 获取当前登录的UserId
            /// 当无用户登录时,值为null
            /// </summary>
            long? UserId { get; }
    
            /// <summary>
            /// Gets current TenantId or null.
            /// This TenantId should be the TenantId of the <see cref="UserId"/>.
            /// It can be null if given <see cref="UserId"/> is a host user or no user logged in.
            /// 获取当前登录的租户Id,
            /// 当无用户登录或是host是,值为null
            /// </summary>
            int? TenantId { get; }
    
            /// <summary>
            /// Gets current multi-tenancy side.
            /// 获取当前多租户参数
            /// </summary>
            MultiTenancySides MultiTenancySide { get; }
    
            /// <summary>
            /// UserId of the impersonator.
            /// This is filled if a user is performing actions behalf of the <see cref="UserId"/>.
            /// </summary>
            long? ImpersonatorUserId { get; }
    
            /// <summary>
            /// TenantId of the impersonator.
            /// This is filled if a user with <see cref="ImpersonatorUserId"/> performing actions behalf of the <see cref="UserId"/>.
            /// </summary>
            int? ImpersonatorTenantId { get; }
    
            /// <summary>
            /// Used to change <see cref="TenantId"/> and <see cref="UserId"/> for a limited scope.
            /// 在限定区域内改变租户id和用户id
            /// </summary>
            /// <param name="tenantId"></param>
            /// <param name="userId"></param>
            /// <returns></returns>
            IDisposable Use(int? tenantId, long? userId);
        }
    

    2. 定义会话基类

    会话基类主要提供虚属性和方法,以便子类重写,实现切换userid和tenantid方法

    /// <summary>
        /// ABPSession基类
        /// </summary>
        public abstract class AbpSessionBase : IAbpSession
        {
            /// <summary>
            /// 覆盖会话上下文的key
            /// </summary>
            public const string SessionOverrideContextKey = "Abp.Runtime.Session.Override";
    
            /// <summary>
            /// 多租户配置
            /// </summary>
            public IMultiTenancyConfig MultiTenancy { get; }
    
            /// <summary>
            /// 当前用户Id
            /// </summary>
            public abstract long? UserId { get; }
    
            /// <summary>
            /// 当前租户Id
            /// </summary>
            public abstract int? TenantId { get; }
    
            public abstract long? ImpersonatorUserId { get; }
    
            public abstract int? ImpersonatorTenantId { get; }
    
            public virtual MultiTenancySides MultiTenancySide
            {
                get
                {
                    return MultiTenancy.IsEnabled && !TenantId.HasValue
                        ? MultiTenancySides.Host
                        : MultiTenancySides.Tenant;
                }
            }
    
            /// <summary>
            /// Session提供者
            /// </summary>
            protected SessionOverride OverridedValue => SessionOverrideScopeProvider.GetValue(SessionOverrideContextKey);
            protected IAmbientScopeProvider<SessionOverride> SessionOverrideScopeProvider { get; }
    
            protected AbpSessionBase(IMultiTenancyConfig multiTenancy, IAmbientScopeProvider<SessionOverride> sessionOverrideScopeProvider)
            {
                MultiTenancy = multiTenancy;
                SessionOverrideScopeProvider = sessionOverrideScopeProvider;
            }
    
            public IDisposable Use(int? tenantId, long? userId)
            {
                return SessionOverrideScopeProvider.BeginScope(SessionOverrideContextKey, new SessionOverride(tenantId, userId));
            }
        }
    

    3. 空对象模式 NullAbpSession

    防止意外null报错

    /// <summary>
        /// Implements null object pattern for <see cref="IAbpSession"/>.
        /// 实现IAbpSession空对象模式
        /// </summary>
        public class NullAbpSession : AbpSessionBase
        {
            /// <summary>
            /// Singleton instance.
            /// </summary>
            public static NullAbpSession Instance { get; } = new NullAbpSession();
    
            /// <inheritdoc/>
            public override long? UserId => null;
    
            /// <inheritdoc/>
            public override int? TenantId => null;
    
            public override MultiTenancySides MultiTenancySide => MultiTenancySides.Tenant;
    
            public override long? ImpersonatorUserId => null;
    
            public override int? ImpersonatorTenantId => null;
    
            private NullAbpSession() 
                : base(
                      new MultiTenancyConfig(), 
                      new DataContextAmbientScopeProvider<SessionOverride>(new AsyncLocalAmbientDataContext())
                )
            {
    
            }
        }
    

    4. 实现从当前Claims中获得会话属性

    该类主要送当前线程的Claims中获取登录这信息

    /// <summary>
        /// Implements <see cref="IAbpSession"/> to get session properties from current claims.
        /// 实现从当前Claims中获得会话属性
        /// </summary>
        public class ClaimsAbpSession : AbpSessionBase, ISingletonDependency
        {
            public override long? UserId
            {
                get
                {
                    if (OverridedValue != null)
                    {
                        return OverridedValue.UserId;
                    }
    
                    var userIdClaim = PrincipalAccessor.Principal?.Claims.FirstOrDefault(c => c.Type == AbpClaimTypes.UserId);
                    if (string.IsNullOrEmpty(userIdClaim?.Value))
                    {
                        return null;
                    }
    
                    long userId;
                    if (!long.TryParse(userIdClaim.Value, out userId))
                    {
                        return null;
                    }
    
                    return userId;
                }
            }
    
            public override int? TenantId
            {
                get
                {
                    if (!MultiTenancy.IsEnabled)
                    {
                        return MultiTenancyConsts.DefaultTenantId;
                    }
    
                    if (OverridedValue != null)
                    {
                        return OverridedValue.TenantId;
                    }
    
                    var tenantIdClaim = PrincipalAccessor.Principal?.Claims.FirstOrDefault(c => c.Type == AbpClaimTypes.TenantId);
                    if (!string.IsNullOrEmpty(tenantIdClaim?.Value))
                    {
                        return Convert.ToInt32(tenantIdClaim.Value);
                    }
    
                    if (UserId == null)
                    {
                        //Resolve tenant id from request only if user has not logged in!
                        return TenantResolver.ResolveTenantId();
                    }
                    
                    return null;
                }
            }
    
            public override long? ImpersonatorUserId
            {
                get
                {
                    var impersonatorUserIdClaim = PrincipalAccessor.Principal?.Claims.FirstOrDefault(c => c.Type == AbpClaimTypes.ImpersonatorUserId);
                    if (string.IsNullOrEmpty(impersonatorUserIdClaim?.Value))
                    {
                        return null;
                    }
    
                    return Convert.ToInt64(impersonatorUserIdClaim.Value);
                }
            }
    
            public override int? ImpersonatorTenantId
            {
                get
                {
                    if (!MultiTenancy.IsEnabled)
                    {
                        return MultiTenancyConsts.DefaultTenantId;
                    }
    
                    var impersonatorTenantIdClaim = PrincipalAccessor.Principal?.Claims.FirstOrDefault(c => c.Type == AbpClaimTypes.ImpersonatorTenantId);
                    if (string.IsNullOrEmpty(impersonatorTenantIdClaim?.Value))
                    {
                        return null;
                    }
    
                    return Convert.ToInt32(impersonatorTenantIdClaim.Value);
                }
            }
    
            protected IPrincipalAccessor PrincipalAccessor { get; }
            protected ITenantResolver TenantResolver { get; }
    
            public ClaimsAbpSession(
                IPrincipalAccessor principalAccessor,
                IMultiTenancyConfig multiTenancy,
                ITenantResolver tenantResolver,
                IAmbientScopeProvider<SessionOverride> sessionOverrideScopeProvider)
                : base(
                      multiTenancy, 
                      sessionOverrideScopeProvider)
            {
                TenantResolver = tenantResolver;
                PrincipalAccessor = principalAccessor;
            }
        }
    

    5. 默认Claims访问器

    通过 访问当前线程 的 CurrentPrincipal 信息获取当前登陆者信息

    /// <summary>
        /// 默认访问器
        /// </summary>
        public class DefaultPrincipalAccessor : IPrincipalAccessor, ISingletonDependency
        {
            /// <summary>
            /// Claims访问器
            /// </summary>
            public virtual ClaimsPrincipal Principal => Thread.CurrentPrincipal as ClaimsPrincipal;
    
            /// <summary>
            /// 单例
            /// </summary>
            public static DefaultPrincipalAccessor Instance => new DefaultPrincipalAccessor();
        }
    

    设计模式

    1. 空对象模式:NullAbpSession
    2. 单例模式:NullAbpSession 、DefaultPrincipalAccessor

    我的公众号

    相关文章

      网友评论

        本文标题:ABP 源码解析 五. Session

        本文链接:https://www.haomeiwen.com/subject/lsxvvftx.html