背景:

公司项目有很多租户,每个租户的系统都可能调用我们的租户服务,原来的解决方案是为每个租户提供一个service。随着租户的增多,service也多了起来,但是每个service里的逻辑都是一样的:验证身份,获取body,调用下游服务。

重构:

现在对外统一提供一个TenantService,里面只有一个Dispatcher方法。现在怎么知道进来的是哪个租户呢,这个租户要调用什么下游服务呢?这里我们用了一个最简单的方法,在Header添加了一个accesskey,我们为每个租户方法提供一个唯一的key,这个key在数据库中存放了对应的租户名,服务名,方法名,身份验证模式等。

本文重点:

本文主要针对不同的租户进来,可能采取不同的身份验证,比如,一个是Basic,另一个又是JWT等等。

首先定义一个特性:CommonAuthenticationAttribute

  [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true, Inherited = true)]
public class CommonAuthenticationAttribute : Attribute, IAuthorizationFilter
{
/// <summary>
/// 1、从数据库中获取相应的租户名,服务名,方法名和身份验证模式
/// 2、将租户名,服务名,方法名添加到请求的header中
/// 3、调用相应的身份验证方法,失败则返回
/// </summary>
/// <param name="context"></param>
public void OnAuthorization(AuthorizationFilterContext context)
{
var accessKey = GetHeaderValue(context, ConstantVar.AccessKey);
//从数据库中获取相应的租户名,服务名,方法名和身份验证模式
var routeData = GetRouteData(accessKey);
//不合法的accesskey
if (routeData == null)
{
var errorMsg = $"Invalidate {ConstantVar.AccessKey} value";
context.Result = new ObjectResult(errorMsg) { StatusCode = 401 };
}
else
{
string authType = routeData.authType;
if (!string.IsNullOrWhiteSpace(authType))
{
//重点:数据库中的authType的值一定要为已经实现的验证模式名字,
var res = context.HttpContext.AuthenticateAsync(authType).Result;
if (!res.Succeeded)//身份验证失败
{
context.Result = new ObjectResult(res.Failure.Message) { StatusCode = 401 };
}
}
}
}
}

以实现Basic验证为例,指定上面代码里的authType

  public class BasicAuthHandler : AuthenticationHandler<AuthenticationSchemeOptions>
{ public BasicAuthHandler(IOptionsMonitor<AuthenticationSchemeOptions> options,
ILoggerFactory logger,
UrlEncoder encoder,
ISystemClock clock) : base(options, logger, encoder, clock)
{
} /// <summary>
/// 验证用户名与密码
/// </summary>
/// <returns></returns>
protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
{
// 跳过有匿名访问标签 [AllowAnonymous]
var endpoint = Context.GetEndpoint();
if (endpoint?.Metadata?.GetMetadata<IAllowAnonymous>() != null)
return AuthenticateResult.NoResult(); if (!Request.Headers.ContainsKey("Authorization"))
return AuthenticateResult.Fail("Missing Authorization Header"); try
{
var authHeader = AuthenticationHeaderValue.Parse(Request.Headers["Authorization"]);
var credentialBytes = Convert.FromBase64String(authHeader.Parameter);
var credentials = Encoding.UTF8.GetString(credentialBytes).Split(new[] { ':' }, 2);
var username = credentials[0];
var password = credentials[1];
var res = await Authenticate(username, password);//验证用户名密码
if (res)
{
var claims = new[]
{
new Claim(ClaimTypes.Name, username),
};
var identity = new ClaimsIdentity(claims, Scheme.Name);
var principal = new ClaimsPrincipal(identity);
var ticket = new AuthenticationTicket(principal, Scheme.Name); return AuthenticateResult.Success(ticket);
}
else
{
return AuthenticateResult.Fail("Unauthorized");
}
}
catch
{
return AuthenticateResult.Fail("Invalid Authorization Header");
} } }

重点来了,在ConfigureServices里添加身份验证的模式。这里的“BasicAuth”就上面的authType

  services.AddAuthentication()
.AddScheme<AuthenticationSchemeOptions, BasicAuthHandler>("BasicAuth", null);//指定身份验证模式名,这里还可以添加多种验证模式

至此,改造任务就基本完成了。然后只需要在Dispatcher方法上加上[CommonAuthentication]。

以后其它租户进入,只需要提供给它一个唯一accesskey就可以了,如果有不同的身份验证方法,添加必要的验证模式就可以了。

最新文章

  1. JSP 标准标签库(JSTL)
  2. JAVA面试逻辑题1
  3. 手把手教你Windows下Go语言的环境搭建
  4. C++ 类的前向声明
  5. 【HDOJ】2266 How Many Equations Can You Find
  6. JNI Java调用C代码 示例
  7. 知方可补不足~CSS中的几个伪元素
  8. 《UNIX环境高级编程》笔记--文件访问权限和新文件、目录所有权
  9. Swift - 使用atlas图集实现动画效果(SpriteKit游戏开发)
  10. Hadoop作业提交之TaskTracker获取Task
  11. LwIP之socket应用--WebServer和Modbus TCP
  12. js获取样式、currentStyle和getComputedStyle的兼容写法
  13. Mac OS X安装native gem提示找不到 dyld_stub_binding_helper
  14. python列表的学习笔记
  15. centos7将可执行程序做成服务
  16. 多线程情况下HashMap死循环的问题
  17. 4. Configure maven in Spring Tool Suite
  18. OSG学习:使用OSG中预定义的几何体
  19. mybatis模糊查询语句
  20. 标准差分进化算法matlab程序实现(转载)

热门文章

  1. Emgu.CV怎么加载Bitmap
  2. 多线程循环打印数组 -- Java笔记
  3. matlab中fspecial Create predefined 2-D filter以及中值滤波均值滤波以及高斯滤波
  4. 利用rtklib处理GPS以及北斗数据详解
  5. Python操作图像
  6. Multipath QUIC (MPQUIC): Design and Evaluation
  7. github 如何解决error: failed to push some refs
  8. hugo不蒜子统计数量
  9. phpstorm 注解路由插件
  10. ansible:安装nginx1.18.0(使用role功能)