ABP 源码解析 五. Session
2018-08-22 本文已影响36人
诸葛_小亮
介绍
此系列文章主要是对ABP源码进行解读,初探作者在创造ABP的框架思路,和使用到的设计模式进行。
通过解读ABP源码,可以提升ABP使用方式,可以提升编码意识,提高面向对象编程思想。
在《ABP 源码解析 四. 启动配置》中介绍了ABP的启动配置功能。
本篇文章主要介绍ABP框架的Session机制。
参考文章:
- 理解ASP.NET Core验证模型(Claim, ClaimsIdentity, ClaimsPrincipal)不得不读的英文博文(https://www.cnblogs.com/dudu/p/6367303.html)
- 空对象模式(https://www.cnblogs.com/hihtml5/p/6411161.html)
UML
![](https://img.haomeiwen.com/i1397128/eb386953cdfb557d.png)
- 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();
}
设计模式
- 空对象模式:NullAbpSession
- 单例模式:NullAbpSession 、DefaultPrincipalAccessor
![](https://img.haomeiwen.com/i1397128/9a9b8a692b08f2be.jpg)