ocelot 自定义认证和授权

Intro

最近又重新启动了网关项目,服务越来越多,每个服务都有一个地址,这无论是对于前端还是后端开发调试都是比较麻烦的,前端需要定义很多 baseUrl,而后端需要没有代码调试的时候需要对每个服务的地址都收藏着或者记在哪里,用的时候要先找到地址,甚是麻烦,有了网关之后,所有的 API 就有了统一的入口,对于前端来说就不需要维护那么多的 baseUrl,只需要网关的地址即可,对于后端来说也是同样的。

Ocelot 简介

Ocelot是一个用.NET Core实现并且开源的API网关,它功能强大,包括了:路由、请求聚合、服务发现、认证、鉴权、限流熔断等功能,这些功能只都只需要简单的配置即可完成。

自定义认证授权

自定义认证授权思想,这里的示例是一个基于用户角色授权的示例:

  1. 基于 url 以及 请求 Method 查询需要的权限
  2. 如果不需要用户登录就可以访问,就直接往下游服务转发
  3. 如果需要权限,判断当前登录用户的角色是否可以以当前 Method 访问当前路径
  4. 如果可以访问就转发到下游服务,如果没有权限访问根据用户是否登录,已登录返回 403 Forbidden,未登录返回 401 Unauthorized

Ocelot 的 认证授权不能满足我的需要,于是就自己扩展了一个 Ocelot 的中间件

示例代码

    public class ApiPermission
{
public string AllowedRoles { get; set; } public string PathPattern { get; set; } public string Method { get; set; }
} public class UrlBasedAuthenticationMiddleware : Ocelot.Middleware.OcelotMiddleware
{
private readonly IConfiguration _configuration;
private readonly IMemoryCache _memoryCache;
private readonly OcelotRequestDelegate _next; public UrlBasedAuthenticationMiddleware(OcelotRequestDelegate next, IConfiguration configuration, IMemoryCache memoryCache, IOcelotLoggerFactory loggerFactory) : base(loggerFactory.CreateLogger<UrlBasedAuthenticationMiddleware>())
{
_next = next; _configuration = configuration;
_memoryCache = memoryCache;
} public async Task Invoke(DownstreamContext context)
{
var permissions = await _memoryCache.GetOrCreateAsync("ApiPermissions", async entry =>
{
using (var conn = new SqlConnection(_configuration.GetConnectionString("ApiPermissions")))
{
entry.AbsoluteExpirationRelativeToNow = TimeSpan.FromHours(1);
return (await conn.QueryAsync<ApiPermission>("SELECT * FROM dbo.ApiPermissions")).ToArray();
}
}); var result = await context.HttpContext.AuthenticateAsync(context.DownstreamReRoute.AuthenticationOptions.AuthenticationProviderKey);
context.HttpContext.User = result.Principal; var user = context.HttpContext.User;
var request = context.HttpContext.Request; var permission = permissions.FirstOrDefault(p =>
request.Path.Value.Equals(p.PathPattern, StringComparison.OrdinalIgnoreCase) && p.Method.ToUpper() == request.Method.ToUpper()); if (permission == null)// 完全匹配不到,再根据正则匹配
{
permission =
permissions.FirstOrDefault(p =>
Regex.IsMatch(request.Path.Value, p.PathPattern, RegexOptions.IgnoreCase) && p.Method.ToUpper() == request.Method.ToUpper());
} if (!user.Identity.IsAuthenticated)
{
if (permission != null && string.IsNullOrWhiteSpace(permission.AllowedRoles)) //默认需要登录才能访问
{
//context.HttpContext.User = new ClaimsPrincipal(new ClaimsIdentity(new[] { new Claim(ClaimTypes.Name, "Anonymous") }, context.DownstreamReRoute.AuthenticationOptions.AuthenticationProviderKey));
}
else
{
SetPipelineError(context, new UnauthenticatedError("unauthorized, need login"));
return;
}
}
else
{
if (!string.IsNullOrWhiteSpace(permission?.AllowedRoles) &&
!permission.AllowedRoles.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).Any(r => user.IsInRole(r)))
{
SetPipelineError(context, new UnauthorisedError("forbidden, have no permission"));
return;
}
} await _next.Invoke(context);
}
}

认证授权之后

经过上面的认证授权之后,就可以往下游转发请求了,下游的服务有的可能会需要判断用户的角色或者需要根据用户的 userId 或者 Name 或者 邮箱去检查某些数据的权限,这里就需要把在网关完成认证之后,得到的用户信息传递给下游服务,这里我选择的是通过请求头把用户信息从网关服务传递到下游服务, Ocelot 可以把 Claims 中的信息转换到 Header ,详细参考Ocelot文档,但是实现有个bug,如果有多个值他只会取第一个,详见issue,可以自己扩展一个 ocelot 的中间件替换掉原有的中间件。

传递到下游服务之后,下游服务在需要用户信息的地方就可以从请求头中获取用户信息,如果下游服务比较复杂,不方便改动的话可以实现一个自定义的请求头认证,可以参考我的这一篇文章

Memo

如果有什么问题或建议,欢迎提出一起交流

最新文章

  1. 【手记】为windows2008建个睡眠快捷方式
  2. 【译】Activitys, Threads和 内存泄露
  3. ThreadLocal深入理解一
  4. HTML5、CSS3各浏览器兼容性
  5. swift基础--字符串
  6. SQL 正则表达式使模式匹配和数据提取变得更容易
  7. Android-Ant自动编译打包android项目 -- 2 ----签名与渠道包
  8. 【ADT】链表的基本C语言实现
  9. private关键字实现控制新建类数量
  10. weblogic-部署web应用
  11. JAVA并发编程学习笔记------协作对象之间发生的死锁
  12. hdu5925 Coconuts
  13. Android里透明的ListView
  14. [20170825]不启动监听远程能连接数据库吗2.txt
  15. bzoj千题计划263:bzoj4870: [六省联考2017]组合数问题
  16. JSON.stringify、JSON.parse、toJSON 区别
  17. 《selenium2 python 自动化测试实战》(8)——定位iframe
  18. centos7 修改主机名(hostnamectl)
  19. 关于Axure RP
  20. CURLOPT_SSL_VERIFYPEER CURLOPT_SSL_VERIFYHOST

热门文章

  1. Mybatis增删改查,Demo整合
  2. netcore服务程序暴力退出导致的业务数据不一致的一种解决方案(优雅退出)
  3. Android版数据结构与算法(四):基于哈希表实现HashMap核心源码彻底分析
  4. MSSQL 更改表结构
  5. 使用async await 封装 axios
  6. jersey在 spring boot 添加 packages 扫描路径支持
  7. ASP.NET Core Web API 集成测试中使用 Bearer Token
  8. Java的二分搜索树
  9. MyX5TbsPlusDemo【体验腾讯浏览服务Android SDK (TbsPlus 版)】
  10. 值得一看的35个Redis常用问题总结