- VisualStudio2022插件的安装及使用-编程手把手系列文章
- pprof-在现网场景怎么用
- C#实现的下拉多选框,下拉多选树,多级节点
- 【学习笔记】基础数据结构:猫树
背景需求:
系统需要对接到XXX官方的API,但因此官方对接以及管理都十分严格。而本人部门的系统中包含诸多子系统,系统间为了稳定,程序间多数固定Token+特殊验证进行调用,且后期还要提供给其他兄弟部门系统共同调用.
原则上:每套系统都必须单独接入到官方,但官方的接入复杂,还要官方指定机构认证的证书等各种条件,此做法成本较大.
so:
为了解决对接的XXX官方API问题,我们搭建了一套中继系统,顾名思义:就是一套用于请求中转的中继系统。在系统搭建的时,Leader提出要做多套鉴权方案,必须做到 动静结合 身份鉴权.
动静结合:就是动态Token 和 静态固定Token.
动态Token:用于兄弟部门系统或对外访问到此中继系统申请的Token,供后期调用对应API.
固定Token:用于当前部门中的诸多子系统,提供一个超级Token,此Token长期有效,且不会随意更换.
入坑:
因为刚来第一周我就接手了这个项目。项目处于申请账号阶段,即将进入开发。对接的是全英文文档(申请/对接流程/开发API....),文档复杂。当时我的感觉:OMG,这不得跑路?整个项目可谓难度之大。然后因为对内部业务也不熟悉,上手就看了微服务等相关系统代码,注:每套系统之间文档少的可怜,可以说系统无文档状态.
项目移交的时候,Leader之说让我熟悉并逐渐进入开发,让我请教同事。好嘛,请教了同事。同事也是接了前任离职的文档而已,大家都不是很熟悉。于是同事让我启新的项目也是直接对接微服务形式开发,一顿操作猛如虎.
项目开发第二周,已经打出框架模型并对接了部分API。此时,Leader开会问进度,结果来一句:此项目使用独立API方式运行,部署到Docker,不接入公司的微服务架构。好嘛,几天功夫白费了,真是取其糟粕去其精华~,恢复成WebAPI.
技术实现:
因为之前对身份认证鉴权这一块没有做太多的深入了解,Leader工期也在屁股追,就一句话:怎么快怎么来,先上后迭代。好嘛,为了项目方便,同时为了符合动静结合的身份认证鉴权 。于是,我用了 JWT+自定义身份认证 实现了需求.
方案一:多身份认证+中间件模式实现 。
添加服务:Services.AddAuthentication 默认使用JWT 。
//多重身份认证
//默认使用JWT,如果Controller使用 AuthenticationSchemes 则采用指定的身份认证
Services.AddAuthentication(options =>
{
options.AddScheme<CustomAuthenticationHandler>(CustomAuthenticationHandler.AuthenticationSchemeName, CustomAuthenticationHandler.AuthenticationSchemeName);
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(options =>
{
options.RequireHttpsMetadata = false;//设置元数据地址或权限是否需要HTTPs
options.SaveToken = true;
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
ValidIssuer = builder.Configuration["Jwt:Issuer"],
ValidAudience = builder.Configuration["Jwt:Audience"],
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(builder.Configuration["Jwt:SecretKey"]!))
};
options.Events = new CustomJwtBearerEvents();
});
自定义身份认证 CustomAuthenticationHandler.cs代码 。
public class CustomAuthenticationHandler : AuthenticationHandler<AuthenticationSchemeOptions>
{
public const string AuthenticationSchemeName = "CustomAuthenticationHandler";
private readonly IConfiguration _configuration;
public CustomAuthenticationHandler(
IOptionsMonitor<AuthenticationSchemeOptions> options,
ILoggerFactory logger,
UrlEncoder encoder,
ISystemClock clock,
IConfiguration configuration)
: base(options, logger, encoder, clock)
{
_configuration = configuration;
}
/// <summary>
/// 固定Token认证
/// </summary>
/// <returns></returns>
protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
{
string isAnonymous = Request.Headers["IsAnonymous"].ToString();
if (!string.IsNullOrEmpty(isAnonymous))
{
bool isAuthenticated = Convert.ToBoolean(isAnonymous);
if (isAuthenticated)
return AuthenticateResult.NoResult();
}
string authorization = Request.Headers["Authorization"].ToString();
// "Bearer " --> Bearer后面跟一个空格
string token = authorization.StartsWith("Bearer ") ? authorization.Remove(0, "Bearer ".Length) : authorization;
if (string.IsNullOrEmpty(token))
return AuthenticateResult.Fail("请求头Authorization不允许为空。");
//通过密钥,进行加密、解密对比认证
if (!VerifyAuthorization(token))
return AuthenticateResult.Fail("传入的Authorization身份验证失败。");
return AuthenticateResult.Success(GetTicket());
}
private AuthenticationTicket GetTicket()
{
// 验证成功,创建身份验证票据
var claims = new[]
{
new Claim(ClaimTypes.Role, "Admin"),
new Claim(ClaimTypes.Role, "Public"),
};
var identity = new ClaimsIdentity(claims, Scheme.Name);
var principal = new ClaimsPrincipal(identity);
var ticket = new AuthenticationTicket(principal, new AuthenticationProperties(), this.Scheme.Name);
return ticket;
}
private bool VerifyAuthorization(string token)
{
//token: [0]随机生成64位字符串,[1]载荷数据,[2]采用Hash对[0]+[1]的签名
var tokenArr = token.Split('.');
if (tokenArr.Length != 3)
{
return false;
}
try
{
//1、先比对签名串是否一致
string signature = tokenArr[1].Hmacsha256HashEncrypt().ToLower();
if (!signature.Equals(tokenArr[2].ToLower()))
{
return false;
}
//解密
var aecStr = tokenArr[1].Base64ToString();
var clientId = aecStr.DecryptAES();
//2、再验证载荷数据的有效性
var clientList = _configuration.GetSection("FixedClient").Get<List<FixedClientSet>>();
var clientData = clientList.SingleOrDefault(it => it.ClientID.Equals(clientId));
if (clientData == null)
{
return false;
}
}
catch (Exception)
{
throw;
}
return true;
}
}
使用中间件:UseMiddleware 。
app.UseAuthentication();
//中间件模式:自定义认证中间件:双重认证选其一
//如果使用 策略,需要注释掉 中间件
app.UseMiddleware<FallbackAuthenticationMiddleware>(); //使用中间件实现
app.UseAuthorization();
中间件FallbackAuthenticationMiddleware.cs代码实现 。
public class FallbackAuthenticationMiddleware
{
private readonly RequestDelegate _next;
private readonly IAuthenticationSchemeProvider _schemeProvider;
public FallbackAuthenticationMiddleware(RequestDelegate next, IAuthenticationSchemeProvider schemeProvider)
{
_next = next;
_schemeProvider = schemeProvider;
}
/// <summary>
/// 身份认证方案
/// 默认JWT。JWT失败,执行自定义认证
/// </summary>
/// <param name="context"></param>
/// <returns></returns>
public async Task InvokeAsync(HttpContext context)
{
var endpoints = context.GetEndpoint();
if (endpoints == null || !endpoints.Metadata.OfType<IAuthorizeData>().Any() || endpoints.Metadata.OfType<IAllowAnonymous>().Any())
{
await _next(context);
return;
}
//默认JWT。JWT失败,执行自定义认证
var result = await Authenticate_JwtAsync(context);
if (!result.Succeeded)
result = await Authenticate_CustomTokenAsync(context);
// 设置认证票据到HttpContext中
if (result.Succeeded)
context.User = result.Principal;
await _next(context);
}
/// <summary>
/// JWT的认证
/// </summary>
/// <param name="context"></param>
/// <returns></returns>
private async Task<dynamic> Authenticate_JwtAsync(HttpContext context)
{
var verify = context.User?.Identity?.IsAuthenticated ?? false;
string authenticationType = context.User.Identity.AuthenticationType;
if (verify && authenticationType != null)
{
return new { Succeeded = verify, Principal = context.User, Message = "" };
}
await Task.CompletedTask;
// 找不到JWT身份验证方案,或者无法获取处理程序。
return new { Succeeded = false, Principal = new ClaimsPrincipal { }, Message = "JWT authentication scheme not found or handler could not be obtained." };
}
/// <summary>
/// 自定义认证
/// </summary>
/// <param name="context"></param>
/// <returns></returns>
private async Task<dynamic> Authenticate_CustomTokenAsync(HttpContext context)
{
// 自定义认证方案的名称
var customScheme = "CustomAuthenticationHandler";
var fixedTokenHandler = await context.RequestServices.GetRequiredService<IAuthenticationHandlerProvider>().GetHandlerAsync(context, customScheme);
if (fixedTokenHandler != null)
{
var Res = await fixedTokenHandler.AuthenticateAsync();
return new { Res.Succeeded, Res.Principal, Res.Failure?.Message };
}
//找不到CustomAuthenticationHandler身份验证方案,或者无法获取处理程序。
return new { Succeeded = false, Principal = new ClaimsPrincipal { }, Message = "CustomAuthenticationHandler authentication scheme not found or handler could not be obtained." };
}
}
方案二:通过[Authorize]标签的AuthenticationSchemes 因为中间件还要多维护一段中间件的代码,显得略微复杂,于是通过[Authorize(AuthenticationSchemes = "")]方式.
//使用特定身份认证
//[Authorize(AuthenticationSchemes = CustomAuthenticationHandler.AuthenticationSchemeName)]
//任一身份认证
[Authorize(AuthenticationSchemes = $"{CustomAuthenticationHandler.AuthenticationSchemeName},{JwtBearerDefaults.AuthenticationScheme}")]
public class DataProcessingController : ControllerBase
{
}
方案二:通过[Authorize]标签的policy 。
如果还有其他身份认证,那不断增加AuthenticationSchemes拼接在Controller的头顶,显得不太好看,且要是多个Controller使用,也会导致维护麻烦,于是改用策略方式.
在Program.cs添加服务AddAuthorization。使用策略的好处是增加易维护性.
//授权策略
//Controller使用 policy 则采用指定的策略配置进行身份认证
builder.Services.AddAuthorization(option =>
{
option.AddPolicy(CustomPolicy.Policy_A, policy => policy
.RequireAuthenticatedUser()
.AddAuthenticationSchemes(CustomAuthenticationHandler.AuthenticationSchemeName, JwtBearerDefaults.AuthenticationScheme)
);
option.AddPolicy(CustomPolicy.Policy_B, policy => policy
.RequireAuthenticatedUser()
.AddAuthenticationSchemes(CustomAuthenticationHandler.AuthenticationSchemeName)
);
option.AddPolicy(CustomPolicy.Policy_C, policy => policy
.RequireAuthenticatedUser()
.AddAuthenticationSchemes(JwtBearerDefaults.AuthenticationScheme)
);
});
//使用特定策略身份认证
[Authorize(policy:CustomPolicy.Policy_B)]
public class DataProcessingController : ControllerBase
{
}
/// <summary>
/// 策略类
/// </summary>
public static class CustomPolicy
{
public const string Policy_A= "Policy_A";
public const string Policy_B = "Policy_B";
public const string Policy_C = "Policy_C";
}
最后附上截图:
添加服务:
使用中间件:
控制器:
这样,整套中继系统就能完美的满足Leader的需求,且达到预期效果.
源码Demo:https://gitee.com/LaoPaoE/project-demo.git 最后附上:
AuthorizeAttribute 同时使用 Policy 和 AuthenticationSchemes 和 Roles 时是怎么鉴权的流程:
鉴权顺序和组合 。
注意事项 。
最后此篇关于NETCore多身份校验与策略模式的文章就讲到这里了,如果你想了解更多关于NETCore多身份校验与策略模式的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
在一个新的 ASP.NET Core RC2 类库中,我有以下 project.json 文件,我试图在其中遵循 How to trim your package dependencies 上的文档.
我在本地提要上将 nuget 包从 2.2.x 更新到 3.1.0。 然后,在构建机器上尝试构建项目,但是: Project.csproj(0,0): Error NU1102: Unable to
快递100的物流信息查询接口,官方提供了一些demo;还好官方提供的代码是.netcore版本写的,不过写的有点low;根据官方提供的代码,我按照.netcore 的风格重构了代码;核心代码如下:
1、docker介绍 docker是用go语言编写基于linux操作系统的一些特性开发的,其提供了操作系统级别的抽象,是一种容器管理技术,它隔离了应用程序对基础架构(操作系统等)的依赖。相较于虚
demo运行在windows的docker中,系统是win10,所以需要先下载docker for windows,安装完毕后系统会重启,然后桌面上可以找到docker for windows的快捷
NetCore WebSocket 即时通讯示例,供大家参考,具体内容如下 1.新建Netcore Web项目 2.创建简易通讯协议 ?
需求场景: 我需要部署的项目是在Windows上开发的,目标框架为.net core 6.0 因此我们需要先在kylin上部署项目运行所需要的环境。 借助百
我正在 .NET Core 中重写一个调用外部 Web 服务的控制台应用程序。 我目前收到以下错误: One or more errors occurred. (The HTTP request is
关闭。此题需要details or clarity 。目前不接受答案。 想要改进这个问题吗?通过 editing this post 添加详细信息并澄清问题. 已关闭 6 年前。 Improve th
我有一组库,我想将其从 PCL 转移到 netcore。通过此举,我想简化 DI 系统并更新一些内部工作方式。 我想添加的其中一件事是内部对象的配置,就像在 Asp.Net Core 中一样(即 se
注:本文隶属于《理解ASP.NET Core》系列文章,请查看置顶博客或 点击此处查看全文目录 概述 在微服务化的架构设计中,网关扮演着重要的看门人角色,它所提供的功能之一
对于有多个应用系统的企业来说,每一个应用系统都有自己的用户体系,这就造成用户在切换不同应用系统时,就要多次输入账号密码,导致体验非常不好,也造成使用上非常不便。 针对这个问题,我们就可以采用单
就像 Web Api 接口可以对入参进行验证,避免用户传入非法的或者不符合我们预期的参数一样,选项也可以对配置源的内容进行验证,避免配置中的值与选项类中的属性不对应或者不满足预期,毕竟大部分配置
.NET Core 选项系统的主要实现在 Microsoft.Extensions.Options 和 Microsoft.Extensions.Options.ConfigurationExten
漏洞说明: 跨站脚本攻击(Cross Site Scripting),为了不和层叠样式表(Cascading Style Sheets, CSS)的缩写混淆,故将跨站脚本攻击缩写为XSS。恶意攻击
分布式缓存是由多个应用服务器共享的缓存,通常作为访问它的应用服务器的外部服务进行维护。 分布式缓存可以提高 ASP.NET Core 应用的性能和可伸缩性,尤其是当应用由云服务或服务器场托管时。
一个应用要运行起来,往往需要读取很多的预设好的配置信息,根据约定好的信息或方式执行一定的行为。 配置的本质就是软件运行的参数,在一个软件实现中需要的参数非常多,如果我们以 Hard Code(
2. 配置添加 配置系统可以读取到配置文件中的信息,那必然有某个地方可以将配置文件添加到配置系统中。之前的文章中讲到 ASP.NET Core 入口文件中,builder(WebApplica
3. 配置提供程序 上面提到,通过 IConfigurationBuilder 的实现类对象,我们可以自由地往配置系统中添加不同的配置提供程序,从而获取不同来源的配置信息。.NET Core
4. 自定义配置提供程序 在 .NET Core 配置系统中封装一个配置提供程序关键在于提供相应的 IconfigurationSource 实现和 IConfigurationProvide
我是一名优秀的程序员,十分优秀!