问题引入:

  通过【ASP.NET Core[源码分析篇] - 认证】这篇文章中,我们知道当请求通过认证模块时,会给当前的HttpContext赋予当前用户身份标识,我们在需要授权的控制器中打上[Authorize]授权标签,就可以在ControllerBase的User属性获取到基于声明的权限标识(ClaimsPrincipal)。

  遗憾的是这只是针对Controller层面,很多场景下我们是需要在Service层乃至数据层获直接使用用户信息,这种情况我们就使用不了User了。  

  解决方案:

  在Asp.net 4.x时代,我们通常的做法是通过HttpContext.Current获取当前请求的上下文进而获取到当前的User属性,所以问题的切入点在于我们如何获取当前的HttpContext上下文。

  在我们的Aspnet Core应用中,系统是通过注入HttpContext的访问器对象IHttpContextAccessor来获取当前的HttpContext。

  实现

  首先我们需要在Startup的ConfigureServices方法中注册IHttpContextAccessor的实例

public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>(); ....
}

  有了这个注册,我们封装一个方法从IHttpContextAccessor 的HttpContext中获取对应的ClaimsPrincipal,如下(认证通过后,User是具有当前用户的身份标志的ClaimsPrincipal):

public class PrincipalAccessor : IPrincipalAccessor
{
  //没有通过认证的,User会为空
public ClaimsPrincipal Principal => _httpContextAccessor.HttpContext?.User; private readonly IHttpContextAccessor _httpContextAccessor; public PrincipalAccessor(IHttpContextAccessor httpContextAccessor)
{
_httpContextAccessor = httpContextAccessor;
}
} public interface IPrincipalAccessor
{
ClaimsPrincipal Principal { get; }
}

  在Startup的ConfigureServices方法中,我们一样把这个类也加入注册中

public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
services.AddSingleton<IPrincipalAccessor, PrincipalAccessor>();
....
}

  有了以上这些基础架构提供的东西,剩下的是我们该需要如何从ClaimsPrincipal获取到对应的Claims了,在这里我们定义一个ClaimsAccessor类负责从PrincipalAccessor把用户的身份标志信息提取出来,比如用户的角色,Id等业务数据,这些是需要在获取Token时系统所提供过的信息。

  public class ClaimsAccessor : IClaimsAccessor
{
protected IPrincipalAccessor PrincipalAccessor { get; } public ClaimsAccessor(IPrincipalAccessor principalAccessor)
{
PrincipalAccessor = principalAccessor;
} /// <summary>
/// 登录用户ID
/// </summary>
public int? ApiUserId
{
get
{
var userId = PrincipalAccessor.Principal?.Claims.FirstOrDefault(c => c.Type == SystemClaimTypes.UserId)?.Value;
if (userId != null)
{
int id = ;
int.TryParse(userId, out id);
return id;
} return null;
}
} /// <summary>
/// 用户角色Id
/// </summary>
public string RoleIds
{
get
{
var roleIds = PrincipalAccessor.Principal?.Claims.FirstOrDefault(c => c.Type == SystemClaimTypes.RoleIds)?.Value;
if (string.IsNullOrWhiteSpace(roleIds))
{
return string.Empty;
} return roleIds;
}
}
  } public interface IClaimsAccessor
{
/// <summary>
/// 登录用户ID
/// </summary>
int? ApiUserId { get; } /// <summary>
/// 用户角色Id
/// </summary>
string RoleIds{ get; }
}

  同样我们在Startup的ConfigureServices方法中把IClaimsAccessor注册进来

public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
services.AddSingleton<IPrincipalAccessor, PrincipalAccessor>();
services.AddSingleton<IClaimsAccessor, ClaimsAccessor>();
....
}

  这样我们所涉及到的需获取身份标志的基础类都已经定义好,来看看我们该如何使用这些基础类。

  使用

  在有了以上的这些类,我们需要的是在业务中通过依赖注入方式来解析出我们所需的对象,来好的让我们看看该如何具体使用  

    public class TestService : ITestService
{
private readonly IClaimsAccessor _claims;
private readonly IRepository<Product> _productRepository; public TestService(IClaimsAccessor claims, IRepository<Product> productRepository)
{
_claims = claims;
_productRepository = productRepository;
} public Result AddProduct(ProductDto dto)
{
_productRepository.Insert(new Product
{
Name = dto.Name,
...
CreatorUserId = _claims.ApiUserId
});
}
}

  当我们的IClaimsAccessor解析出来时,我们就获取到所有的Claims信息,可以基于这些信息提取访问用户的身份标志,这样我们就不仅仅局限于在Controller层面才能获取用户的身份标志了,至此我们的系统级别的标识已经完成,记得在项目的启动项中利用ASP.NET Core的容器把服务注册进来,在需通的地方解析出来即可使用。

  改进

  这时能满足我们的业务吗?能!但是对于我们稍微要求高点的程序员,我们就可以发现,如果每个服务都按照上面的写法的话,明显在实际应用中需要写很多重复的代码,每次都需要手动进行构造注入会比较繁琐,好吧,看看我们该如何进行优雅且可复用的简化和改进

  首先我们先定义一个ServiceProviderInstance类

  public class ServiceProviderInstance
{
public static IServiceProvider Instance { get; set; }
} 

  这个类的作用是保存IServiceProvider的一个实例,为什么需要这样呢?这里的一个设计思想是,我们如何能顺利解析出我们所需的IClaimsAccessor对象进而得到我们所需的信息?在ASP.NET Core的容器中,系统提供了IServiceCollection来注册服务和提供了IServiceProvider这个让我们解析各种注册过的服务(具体可参考ASP.NET Core - 依赖注入文章所讲解的依赖注入),这时我们的目标就是需要获取到当前应用的IServiceProvider实例,所以这个ServiceProviderInstance类的作用时为了获取IServiceProvider所设计出来的静态类。

  如何获取到应用的IServiceProvider实例?

  在应用初始化过程中,WebHostBuilder会利用ServiceCollection来创建新的ServiceProvider来供系统使用,所以我们在Startup类的Configure方法中,通过ApplicationBuilder的ApplicationServices属性就能获取到系统的ServiceProvider实例,在此我们利用ServiceProviderInstance的Instance属性保存当前的IServiceProvider以供系统后面使用  

  public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
...
ServiceProviderInstance.Instance = app.ApplicationServices;
}

  在获取到了系统的IServiceProvider实例后,剩下的就是利用这个实例把我们前面注册的基础服务IClaimsAccessor解析出来了,我们可以利用面向对象的特点,创建基类进行继承

    public abstract class ServiceBase
{
/// <summary>
/// 身份信息
/// </summary>
protected IClaimsAccessor Claims { get; set; } /// <summary>
/// cotr
/// </summary>
protected ServiceBase ()
{
Claims = ServiceProviderInstance.Instance.GetRequiredService<IClaimsAccessor>();
}
}

  好的让我们看看改进后在一个实际环境中该如何使用

   public class TestService : ServiceBase,ITestService
{
private readonly IRepository<Product> _productRepository; public TestService(IRepository<Product> productRepository)
{
_productRepository = productRepository;
} public Result AddProduct(ProductDto dto)
{
_productRepository.Insert(new Product
{
Name = dto.Name,
...
CreatorUserId = Claims.ApiUserId
});
}
}

  让所有子类都继承了ServiceBase,这样在所有的业务层都可以直接获取到用户的身份信息而不用写太多的重复代码。

  总结

  1. 利用ASP.NET Core提供的IHttpContextAccessor来获取HttpContext的User属性

  2. 封装一系列的基础类和利用依赖注入来解析出所有的Claims

  3. 为了避免过多的侵入式代码,优雅且可复用的创建ServiceBase给所有的业务类使用

  让我知道如果你有更好的想法或建议!

ASP.NET Core - 基于IHttpContextAccessor实现系统级别身份标识的更多相关文章

  1. ASP.NET Core 基于JWT的认证(一)

    ASP.NET Core 基于JWT的认证(一) Json web token (JWT), 是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准((RFC 7519).该token被设计 ...

  2. ASP.NET Core 基于JWT的认证(二)

    ASP.NET Core 基于JWT的认证(二) 上一节我们对 Jwt 的一些基础知识进行了一个简单的介绍,这一节我们将详细的讲解,本次我们将详细的介绍一下 Jwt在 .Net Core 上的实际运用 ...

  3. Asp.Net Core基于JWT认证的数据接口网关Demo

    近日,应一位朋友的邀请写了个Asp.Net Core基于JWT认证的数据接口网关Demo.朋友自己开了个公司,接到的一个升级项目,客户要求用Aps.Net Core做数据网关服务且基于JWT认证实现对 ...

  4. ASP.NET Core基于K8S的微服务电商案例实践--学习笔记

    摘要 一个完整的电商项目微服务的实践过程,从选型.业务设计.架构设计到开发过程管理.以及上线运维的完整过程总结与剖析. 讲师介绍 产品需求介绍 纯线上商城 线上线下一体化 跨行业 跨商业模式 从0开始 ...

  5. 如何在ASP.NET Core中实现一个基础的身份认证

    注:本文提到的代码示例下载地址> How to achieve a basic authorization in ASP.NET Core 如何在ASP.NET Core中实现一个基础的身份认证 ...

  6. [转]如何在ASP.NET Core中实现一个基础的身份认证

    本文转自:http://www.cnblogs.com/onecodeonescript/p/6015512.html 注:本文提到的代码示例下载地址> How to achieve a bas ...

  7. Asp.net Core基于MVC框架实现PostgreSQL操作

    简单介绍 Asp.net Core最大的价值在于跨平台.跨平台.跨平台.重要的事情说三遍.但是目前毕竟是在开发初期,虽然推出了1.0.0 正式版,但是其实好多功能还没有完善.比方说编译时的一些文件编码 ...

  8. Asp.Net Core基于Cookie实现同域单点登录(SSO)

    在同一个域名下有很多子系统 如:a.giant.com  b.giant.com   c.giant.com等 但是这些系统都是giant.com这个子域. 这样的情况就可以在不引用其它框架的情况下, ...

  9. Asp.Net Core Docker镜像更新系统从wheezy改为stretch

    之前写过一个在Asp.Net Core里调用System.Drawing.Common绘图的DEMO,部署到Docker里运行,需要更新Asp.Net Core镜像的操作系统. https://www ...

随机推荐

  1. Ubuntu 18.04 美化配置

    网上很多关于Ubuntu 18.04 美化的配置,但每个人遇见的问题都有些不太一样, 现将本人配置的过程整理如下 更新源为阿里云 找到Software & Updates,将源更新为阿里云的源 ...

  2. SpringBoot电商项目实战 — 商品的SPU/SKU实现

    最近事情有点多,所以系列文章已停止好多天了.今天我们继续Springboot电商项目实战系列文章.到目前为止,整个项目的架构和基础服务已经全部实现,分布式锁也已经讲过了.那么,现在应该到数据库设计及代 ...

  3. Python读写Excel表格(简单实用)

    前言 本文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理.作者:giao窝里giao首先安装两个库:pip install xlrd. ...

  4. Python批量更新模块的方法【面试必学】

    前言 本文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理.作者:ranchlin      小编的环境为win10+python 3. ...

  5. swoole如何使php永久运行

    有需要学习交流的友人请加入交流群的咱们一起,有问题一起交流,一起进步!前提是你是学技术的.感谢阅读! 点此加入该群​jq.qq.com soole可以通过开启守护进程使PHP永久运行. 守护进程化.设 ...

  6. C# ThreadPool 线程池

    Thread与ThreadPool Thread: .NetFramework1.0  对线程对象的一个封装 Thread方法很多很强大,但是太过强大,而且没有限制 功能繁多,反而用不好--就像给4岁 ...

  7. jQuery实现下拉框默认选中

    <form class="form-inline" method="post" action="/score_result/"> ...

  8. CSS之border绘制三角形

    用CSS的border可以画出高质量的三角形. 我们一般会这么使用border: #test-border { width: 100px; height: 100px; margin: 100px a ...

  9. [css display],table待续

    昨天复习了flex布局,今天打算继续,才发现有display:table,今天有的点,别的先写上,其他的后补吧 css display // none 此元素不会被显示. // block 此元素将显 ...

  10. 一文解读RESTful (转)

    01 前言 回归正题,看过很多RESTful相关的文章总结,参齐不齐,结合工作中的使用,非常有必要归纳一下关于RESTful架构方式了,RESTful只是一种架构方式的约束,给出一种约定的标准,完全严 ...