ABP

ABP 源码解析 五. Session

2018-08-22  本文已影响36人  诸葛_小亮

介绍

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

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

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

参考文章:


UML

UML

源码解析

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

我的公众号
上一篇 下一篇

猜你喜欢

热点阅读