ASP.NET Core - 实现Http自定义请求头策略
前言
在正常的情况下,当我们系统用到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自定义请求头策略的更多相关文章
- WebAPI调用笔记 ASP.NET CORE 学习之自定义异常处理 MySQL数据库查询优化建议 .NET操作XML文件之泛型集合的序列化与反序列化 Asp.Net Core 轻松学-多线程之Task快速上手 Asp.Net Core 轻松学-多线程之Task(补充)
WebAPI调用笔记 前言 即时通信项目中初次调用OA接口遇到了一些问题,因为本人从业后几乎一直做CS端项目,一个简单的WebAPI调用居然浪费了不少时间,特此记录. 接口描述 首先说明一下,基于 ...
- asp.net core 3.1 自定义中间件实现jwt token认证
asp.net core 3.1 自定义中间件实现jwt token认证 话不多讲,也不知道咋讲!直接上代码 认证信息承载对象[user] /// <summary> /// 认证用户信息 ...
- ASP.NET Core 防止跨站请求伪造(XSRF/CSRF)攻击 (转载)
什么是反伪造攻击? 跨站点请求伪造(也称为XSRF或CSRF,发音为see-surf)是对Web托管应用程序的攻击,因为恶意网站可能会影响客户端浏览器和浏览器信任网站之间的交互.这种攻击是完全有可能的 ...
- asp.net core 使用中间件拦截请求和返回数据,并对数据进行加密解密。
原文:asp.net core 使用中间件拦截请求和返回数据,并对数据进行加密解密. GitHub demo https://github.com/zhanglilong23/Asp.NetCore. ...
- asp.net core 实现支持自定义 Content-Type
asp.net core 实现支持自定义 Content-Type Intro 我们最近有一个原本是内网的服务要上公网,在公网上有一层 Cloudflare 作为网站的公网流量提供者,CloudFla ...
- ASP.NET Core中显示自定义错误页面-增强版
之前的博文 ASP.NET Core中显示自定义错误页面 中的方法是在项目中硬编码实现的,当有多个项目时,就会造成不同项目之间的重复代码,不可取. 在这篇博文中改用middleware实现,并且放在独 ...
- 使用 Spring RestTemplate 调用 rest 服务时自定义请求头(custom HTTP headers)
在 Spring 3.0 中可以通过 HttpEntity 对象自定义请求头信息,如: private static final String APPLICATION_PDF = "app ...
- Asp.net Core 入门实战 2.请求流程
Asp.Net Core 是开源,跨平台,模块化,快速而简单的Web框架. Asp.net Core官网的一个源码合集,方便一次性Clone,喜欢的(Star),本系列持续更新,也可以通过我的网站访问 ...
- Ajax设置自定义请求头的两种方法
用自定义请求头token为例 方法一 $.ajax({ type: "post", url:"http://127.0.0.1:4564/bsky-app/templat ...
随机推荐
- Spring Boot Redis 实现分布式锁,真香!!
之前看很多人手写分布式锁,其实 Spring Boot 现在已经做的足够好了,开箱即用,支持主流的 Redis.Zookeeper 中间件,另外还支持 JDBC. 本篇栈长以 Redis 为例(这也是 ...
- Python Ethical Hacking - NETWORK_SCANNER(2)
DICTIONARIES Similar to lists but use key instead of an index. LISTS List of values/elements, all ca ...
- 题解 CF296B 【Yaroslav and Two Strings】
题目 传送门 题目大意 如果两个只包含数字且长度为 \(n\) 的字符串 \(s\) 和 \(w\) 存在两个数字 \(1≤i,j≤n\),使得 \(s_i<w_i,s_j>w_j\) , ...
- 如何看待HTTP/3
前言 HTTP/2 相比于 HTTP/1.1,可以说是大幅度提高了网页的性能,只需要升级到该协议就可以减少很多之前需要做的性能优化工作,当然兼容问题以及如何优雅降级应该是国内还不普遍使用的原因之一. ...
- 深入了解PHP的生成器
在驾驶方面,速度并不会决定一切.但是在网络上,速度至关重要.你的应用程序越快,用户体验就越好.好吧,这时候有人就奇怪了,本文是关于PHP 生成器的,那么为什么我们要谈论速度呢?很快你就会发现,生成器在 ...
- 题解 CF997E 【Good Subsegments】
先将问题进行转化,发现满足\((max-min)-(r-l)=0\)的区间即为好区间. 对于本题这样的统计子区间的问题,先将询问离线,按右端点排序一个一个解决,固定右端点,然后通过数据结构来处理出区间 ...
- 源码分析清楚 AbstractQueuedSynchronizer
本文关注以下几点内容: 深入理解 ReentrantLock 公平锁和非公平锁的区别 深入分析 AbstractQueuedSynchronizer 中的 ConditionObject 深入理解 j ...
- 修改docker中mysql登入密码(包括容器内和本地远程登入的密码)
查看docker中正在运行的容器 docker ps 进入MySQL 容器中 sudo docker exec -it cd800a1cd503 /bin/bash 在容器中: /etc/mysql/ ...
- 安装 kreas 2.2.4 版本问题
python 版本 3.6 不要使用 3.7 和 3.8 否则导致安装失败 tensorflow 使用 1.12.0 keras 使用 2.2.4 版本 pip install 包名 -i http: ...
- Python字典内置函数和方法
Python字典内置函数和方法: 注:使用了 items.values.keys 返回的是可迭代对象,可以使用 list 转化为列表. len(字典名): 返回键的个数,即字典的长度 # len(字典 ...