前言  

  在正常的情况下,当我们系统用到JWT认证方式时,需要在Http请求头添加Authorization: XXX,这样在后台服务的控制器中打上[Authorize]授权标签,就限定所有的请求必须通过鉴权方可访问。

  在【ASP.NET Core - 基于IHttpContextAccessor实现系统级别身份标识】这篇文章中我们能够注意到,通过IHttpContextAccessor获取基于请求生成的HttpContext后,我们是能够拿到基于该次请求的所有http信息的。这就给予了我们可以基于Http的请求做自定义请求头的策略来满足我们业务的扩展。

  场景

  在不少的场景中,特别是多租户的场景中,我们是需要知道当前是哪个租户下的用户在操作,而我们又不需要把这租户作为业务参数传递,毕竟如果按照业务参数这样的方式去实现的话,就好比从源头传入了一个污染源,需要一路污染下去,这是非常不推荐的方式,这时就可以利用自定义http请求头来传递这个参数,在最终需要用到这个参数的时候获取出来。

  在我们的系统层面,例如我们的应用部署在K8S集群中的网络,在网关层解析了JWT通过认证后放行之后,内网的应用服务可以依赖内网的保护把JWT的认证删减掉(这样在一定程上是可以提高性能的),我们就可以通过以下自定义头的实现方式,把租户信息携带在http头部,一路传递下去。

  实现

  类似于我们在【ASP.NET Core - 基于IHttpContextAccessor实现系统级别身份标识】中的IHttpContextAccessor实现方式一样,我们先定义我们的IHttpHeaderAccessor用来获取HttpHeader

    /// <summary>
/// httpcontext的header
/// </summary>
public interface IHttpHeaderAccessor
{
/// <summary>
/// the httpheader
/// </summary>
IHeaderDictionary HttpHeader { get; }
}

  HttpHeaderAccessor 的实现  

    /// <summary>
/// httpheader访问器
/// </summary>
public class HttpHeaderAccessor : IHttpHeaderAccessor
{
/// <summary>
/// the HttpContextAccessor
/// </summary>
private readonly IHttpContextAccessor _httpContextAccessor; public IHeaderDictionary HttpHeader => _httpContextAccessor.HttpContext.Request.Headers; /// <summary>
/// ctor
/// </summary>
/// <param name="httpContextAccessor">the HttpContextAccessor</param>
public HttpHeaderAccessor(IHttpContextAccessor httpContextAccessor)
{
_httpContextAccessor = httpContextAccessor;
}
}

  定义http请求头的自定义参数

    public interface ICustomHeaderAccessor
{
/// <summary>
/// 租户Id
/// </summary>
string TenantId { get; }
}

  http请求头的自定义参数的获取,这里是直接从头部获取到租户信息

  /// <summary>
/// 获取自定义http头
/// </summary>
public class CustomHeaderAccessor : ICustomHeaderAccessor
{
protected IHttpHeaderAccessor _httpHeaderAccessor { get; } /// <summary>
/// ctor
/// </summary>
/// <param name="httpHeaderAccessor"></param>
public CustomHeaderAccessor(IHttpHeaderAccessor httpHeaderAccessor)
{
_httpHeaderAccessor = httpHeaderAccessor;
} /// <summary>
/// 租户Id
/// </summary>
public string TenantId
{
get
{
return _httpHeaderAccessor.HttpHeader[HeaderConst.TenantId];
}
}
}

  扩展

  在【ASP.NET Core - 基于IHttpContextAccessor实现系统级别身份标识】我们知道IHttpContextAccessor,IHttpHeaderAccessor,ICustomHeaderAccessor都需要注册,有了上面的这些方法定义,我们定义一个ServiceCollection的扩展方法进行注册

    /// <summary>
/// 基于IServiceCollection的扩展类
/// </summary>
public static class ServiceCollectionExtension
{
/// <summary>
/// 注册IHttpContextAccessor,IHttpHeaderAccessor和ICustomHeaderAccessor
/// </summary>
/// <param name="services">the IServiceCollection</param>
/// <returns></returns>
public static IServiceCollection AddCustomHttpHeader(this IServiceCollection services)
{
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
services.AddSingleton<IHttpHeaderAccessor, HttpHeaderAccessor>();
services.AddSingleton<ICustomHeaderAccessor, CustomHeaderAccessor>(); return services;
}
}

  有了服务的注册,我们还需要一个当前服务的实例提供者,一个ServiceProvider,如下

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

  我们在获取到当前进程的ServiceProvider后,保存到本地静态变量

    /// <summary>
/// 基于IApplicationBuilder的扩展
/// </summary>
public static class ServiceProviderExtension
{
/// <summary>
/// 给ServiceProviderInstance赋值ServiceProvider实例
/// </summary>
/// <param name="applicationBuilder">the IApplicationBuilder</param>
/// <returns></returns>
public static IApplicationBuilder UseServiceProviderBulider(this IApplicationBuilder applicationBuilder)
{
ServiceProviderInstance.Instance = applicationBuilder.ApplicationServices; return applicationBuilder;
}
}

  这里为什么能够在进程内保存一个静态的ServiceProvider?

  是因为在应用启动过程中,已经把Ioc容器构建好,把该注册的对象实例关系都已经保存在Ioc容器中了,这时ServiceProvider在进程内是不会有变化的了(动态注册暂时没在本篇文章的使用范围)。

  在这里已经给我们提示了另一种对象实例获取的方式,就是通过这个本地的ServiceProviderInstance来解析自己的所注册的对象实例,而不仅仅是通过构造函数(或者属性)注入的方式。

  使用

  看看我们如何完成整个流程的注册以及使用。

  首选需要在程序启动的时候注册。这里通过注册以及swagger的声明,用来做为本地的调试请求头添加

  public void ConfigureServices(IServiceCollection services)
{
// 注册Swagger
services.AddAppSwagger(document =>
{
document.Description = "API"; document.OperationProcessors.Add(new OperationSecurityScopeProcessor("TenantId")); document.DocumentProcessors.Add(new SecurityDefinitionAppender("TenantId", new NSwag.OpenApiSecurityScheme
{
Type = NSwag.OpenApiSecuritySchemeType.ApiKey,
Name = "TenantId",
In = NSwag.OpenApiSecurityApiKeyLocation.Header,
Description = "租户Id"
})); services.AddHttpHeader();
}

    获取系统的ServiceProvider并保存到本地。

  public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
} app.UseServiceProviderBuilder(); // Swagger中间件
app.UseAppSwagger(); app.UseRouting(); app.UseAuthorization(); app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}

  定义应用服务的抽象类

 public abstract class ServiceBase
{
/// <summary>
/// 自定义头信息
/// </summary>
protected ICustomHeaderAccessor CustomHeader{ get; set; } /// <summary>
/// ctor
/// </summary>
protected ServiceBase ()
{
CustomHeader = ServiceProviderInstance.Instance.GetRequiredService<ICustomHeaderAccessor>();
}
}

  使用层面上的赋值

  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,
...
TenantId = CustomHeader.TenantId
});
}
}

  Swagger的本地调用

  为了方便调试,我们在上面的注册代码中写入了通过swagger中的请求头携带的http头部请求信息,在每次的本地调用中,可通过以下方式把我们需要传递的自定义信息填充进去。

ASP.NET Core - 实现Http自定义请求头策略的更多相关文章

  1. WebAPI调用笔记 ASP.NET CORE 学习之自定义异常处理 MySQL数据库查询优化建议 .NET操作XML文件之泛型集合的序列化与反序列化 Asp.Net Core 轻松学-多线程之Task快速上手 Asp.Net Core 轻松学-多线程之Task(补充)

    WebAPI调用笔记   前言 即时通信项目中初次调用OA接口遇到了一些问题,因为本人从业后几乎一直做CS端项目,一个简单的WebAPI调用居然浪费了不少时间,特此记录. 接口描述 首先说明一下,基于 ...

  2. asp.net core 3.1 自定义中间件实现jwt token认证

    asp.net core 3.1 自定义中间件实现jwt token认证 话不多讲,也不知道咋讲!直接上代码 认证信息承载对象[user] /// <summary> /// 认证用户信息 ...

  3. ASP.NET Core 防止跨站请求伪造(XSRF/CSRF)攻击 (转载)

    什么是反伪造攻击? 跨站点请求伪造(也称为XSRF或CSRF,发音为see-surf)是对Web托管应用程序的攻击,因为恶意网站可能会影响客户端浏览器和浏览器信任网站之间的交互.这种攻击是完全有可能的 ...

  4. asp.net core 使用中间件拦截请求和返回数据,并对数据进行加密解密。

    原文:asp.net core 使用中间件拦截请求和返回数据,并对数据进行加密解密. GitHub demo https://github.com/zhanglilong23/Asp.NetCore. ...

  5. asp.net core 实现支持自定义 Content-Type

    asp.net core 实现支持自定义 Content-Type Intro 我们最近有一个原本是内网的服务要上公网,在公网上有一层 Cloudflare 作为网站的公网流量提供者,CloudFla ...

  6. ASP.NET Core中显示自定义错误页面-增强版

    之前的博文 ASP.NET Core中显示自定义错误页面 中的方法是在项目中硬编码实现的,当有多个项目时,就会造成不同项目之间的重复代码,不可取. 在这篇博文中改用middleware实现,并且放在独 ...

  7. 使用 Spring RestTemplate 调用 rest 服务时自定义请求头(custom HTTP headers)

    在 Spring 3.0 中可以通过  HttpEntity 对象自定义请求头信息,如: private static final String APPLICATION_PDF = "app ...

  8. Asp.net Core 入门实战 2.请求流程

    Asp.Net Core 是开源,跨平台,模块化,快速而简单的Web框架. Asp.net Core官网的一个源码合集,方便一次性Clone,喜欢的(Star),本系列持续更新,也可以通过我的网站访问 ...

  9. Ajax设置自定义请求头的两种方法

    用自定义请求头token为例 方法一 $.ajax({ type: "post", url:"http://127.0.0.1:4564/bsky-app/templat ...

随机推荐

  1. C#生成Excel文档(EPPlus)

    1.公式计算 worksheet.Cells["D2:D5"].Formula = "B2*C2";//这是乘法的公式,意思是第二列乘以第三列的值赋值给第四列, ...

  2. Flink之对时间的处理

    window+trigger+watermark处理全局乱序数据,指定窗口上的allowedLateness可以处理特定窗口操作的局部事件时间乱序数据 1.流处理系统中的微批 Flink内部也使用了某 ...

  3. Qt_IO系统_二进制读写

    目录 Qt中的读写 QDataStream QDataStream Qt 是如何存储数据的 如何保证读取数据的正确性? --> 魔术数字,文件版本,Qt版本 魔术数字 文件版本 Qt 版本 读取 ...

  4. Python Ethical Hacking - MAC Address & How to Change(2)

    FUNCTIONS Set of instructions to carry out a task. Can take input, and return a result. Make the cod ...

  5. window下远程连接redis服务

    首先下redis包: 下载地址:https://github.com/MSOpenTech/redis/releases. 之后: 1.注释掉redis.windows-service.conf 中的 ...

  6. CodeForces - 722C Destroying Array (并查集/集合的插入和删除)

    原题链接:https://vjudge.net/problem/511814/origin Description: You are given an array consisting of n no ...

  7. Andriod开发---《横竖屏切换时 Activity的生命周期的总结》

    横屏切换竖屏Activity的生命周期详解,下面分析一下切换时具体的生命周期: 1.新建一个Activity,并把各个生命周期打印出来 2.运行Activity,得到如下信息 onCreate--&g ...

  8. 初识ES数据库

    一.什么是Elasticsearch 1.概念以及特点 1.Elasticsearch和MongoDB/Redis/Memcache一样,是非关系型数据库. 是一个接近实时的搜索平台,从索引这个文档到 ...

  9. 解决移动端rem加载瞬间页面错乱的方法(放大或者缩小)

    移动端布局有很多种,这里我们最常使用到rem+百分比的布局方式(高度/字体设置rem单位,宽度设置百分比)来处理屏幕兼容,这种方法在兼容上是比较不错的,可以使得字体以及整体适应各种大小的屏幕,可以解决 ...

  10. matplotlib 去掉坐标轴

    #去掉x轴 plt.xticks([]) #去掉y轴 plt.yticks([]) #去掉坐标轴 plt.axis('off') 2020-06-26