做地方特产的网站,唯尚广告联盟,学校网站建设步骤过程,淄博企业建网站书接上文#xff0c;上回咱们说到了《【Blog.Core开源】网关统一集成下游服务文档》#xff0c;已经将多个下游服务统一集成到了网关里#xff0c;并且也把接口文档Swagger给集成了#xff0c;那今天就说一下认证和鉴权相关的话题。继续说下故事背景在平时开发的时候#… 书接上文上回咱们说到了《【Blog.Core开源】网关统一集成下游服务文档》已经将多个下游服务统一集成到了网关里并且也把接口文档Swagger给集成了那今天就说一下认证和鉴权相关的话题。继续说下故事背景在平时开发的时候特别是有网关的情况下经常会遇到一个不可避免的话题就是网关到底要不要帮下游处理某些业务逻辑的问题比如说认证鉴权、审计日志、当前用户信息获取白名单等等。这里其实见仁见智同时也要考虑各个项目的具体架构设计和需要我个人的习惯还是网关要轻一些什么叫轻一些呢拿BlogCore举例认证走的是Ids4的统一认证平台从平台那里得到令牌Token然后经过网关走到BlogCore解析并走具体的自定义授权逻辑因为这里涉及到动态菜单权限配置所以很少会放到网关里处理毕竟每个下游服务都可能会有自己的那部分逻辑。其实除了授权这块还有一些数据比如当前用户的私密信息例如手机号之类的这个phone肯定不能放到token里的因为token虽然有过期时间但是就算是失效还是可以解密出来的放到公网上的令牌基本都是只放一些非私密的个人信息比如uid或者是roleId实在有需要也可以在token里放部门的id的这也无可厚非但是phone和address是万万不能放到token里的。那么问题来了phone和address我们到底应该从哪里获取上边的菜单权限大家已经达成共识就是放到下游让下游服务自己来处理那根据token中的uid来获取phone信息就需要考虑下了很多人说放网关呗每次请求查库等操作然后放到header里传递给下游这也是一个方案今天也会给大家讲讲怎么获取怎么传。当然我个人的意见还是网关仅仅是解析token里有的传递给下游至于查库的那些还是下游获取吧这是我的个人意见并不是完全正确。为什么呢大家想想咱们在网关里写拦截器或者中间件每次接口请求都根据header中的token来查库这样不管下游需不需要不管下游接口是不是匿名都去查库一下会造成资源浪费比如我就想搜索下list每次都查询下当前人的user信息似乎没那么必要特别是list页面高并发的时候是不是不太好当然这样的好处就是对下游方便且能做详细的审计日志。今天咱们就说下如何自定义拦截器传递自定义claim信息给下游。01PART网关自定义认证处理器在网关中注册认证服务并设计处理器实现认证授权拦截比如说token是否可以正常的解密等用来判断token的有效性等也可以查询数据库获取私密信息services.AddAuthentication().AddSchemeAuthenticationSchemeOptions, CustomAuthenticationHandler(Permissions.GWName, _ { });然后具体的处理器大家根据需求自定义即可注意把信息放到Claims里不仅可以在当前网关的其他地方获取从而减少二次请求的情况。也可以传递给下游服务。public class CustomAuthenticationHandler : AuthenticationHandlerAuthenticationSchemeOptions
{public CustomAuthenticationHandler(IOptionsMonitorAuthenticationSchemeOptions options,ILoggerFactory logger,UrlEncoder encoder,ISystemClock clock) : base(options, logger, encoder, clock){}protected override async TaskAuthenticateResult HandleAuthenticateAsync(){// 可以查询数据库等操作// 获取当前用户不能放到token中的私密信息var userPhone 15010000000;var claims new ListClaim(){new Claim(user-phone, userPhone),new Claim(gw-sign, gw)};var principal new ClaimsPrincipal(new ClaimsIdentity(claims, Scheme.Name));var ticket new AuthenticationTicket(principal, Scheme.Name);await Task.CompletedTask;return AuthenticateResult.Success(ticket);}
} 内容很简单就是一个普通的处理器那接下来就是看如何把Claim给传给下游服务了。02PART对下游服务开启认证处理器Ocelot已经做好了配置就像是自定义响应处理器一样认证的也可以直接配置// blog-svc
{UpstreamPathTemplate: /svc/blog/{url},UpstreamHttpMethod: [ Get, Post, Put, Delete ],LoadBalancerOptions: {Type: RoundRobin},DownstreamPathTemplate: /svc/blog/{url},DownstreamScheme: http,DownstreamHostAndPorts: [{Host: localhost,Port: 9291}],// 直接配置认证Key即可AuthenticationOptions: {AuthenticationProviderKey: GW}
},也可以有更多的参数配置具体可以参考官网https://ocelot.readthedocs.io/en/latest/features/configuration.html?highlightAuthenticationOptions#configuration03PARTOcelot将Claim传递下游还是在Ocelot的官网上可以看到很多Demo我只配置三项1、分别是动态从Claim中获取并用Request的Header传值2、直接在Request中传递固定Header值3、获取下游服务的Response的Header给上游网关。其中第三点还是很有用的比如我们以后的Skywalking中如果某次链路请求报错了但是又想快速的定位所以就需要用户给我们提供当前操作的标识有时候是uid有时候是url这两个都不是很直观。通过配置Ocelot正好可以从下游服务的response的header中返给前端用户就能提供了更加快速方便的定位问题。// blog-svc
{UpstreamPathTemplate: /svc/blog/{url},UpstreamHttpMethod: [ Get, Post, Put, Delete ],LoadBalancerOptions: {Type: RoundRobin},DownstreamPathTemplate: /svc/blog/{url},DownstreamScheme: http,DownstreamHostAndPorts: [{Host: localhost,Port: 9291}],// 添加到headers// 从claims中获取AddHeadersToRequest: {user-phone: Claims[user-phone] value,gw-sign: Claims[gw-sign] value},// 从上游网关的request的header中UpstreamHeaderTransform: {custom-key: blog.gateway},// 从下游服务的response的header中DownstreamHeaderTransform: {down-app: {para-down-app},trace-id: Trace-Id},AuthenticationOptions: {AuthenticationProviderKey: GW}
},在上边注释的三块就是常见的三种方案。04PART下游服务查看具体效果在BlogCore服务中valueController中测试下是否传递了具体的参数[HttpGet]
public MessageModelListClaimDto MyClaims()
{return new MessageModelListClaimDto(){success true,response (_user.GetClaimsIdentity().ToList()).Select(d new ClaimDto{Type d.Type,Value d.Value}).ToList()};
}其中获取Claim方法也获取了下header中其他的参数public IEnumerableClaim GetClaimsIdentity(){var claims _accessor.HttpContext.User.Claims.ToList();var headers _accessor.HttpContext.Request.Headers;foreach (var header in headers){claims.Add(new Claim(header.Key, header.Value));}return claims;}这里有一个小注意事项如果下游服务是加权的可以直接通过swagger添加token的方式获取claims信息但是接口是匿名的那swagger是不会传递token信息的我们可以用postman测试一样的效果毕竟前端Vue.js也是我们手动传递的。关于swagger不加权就不传递token这个问题以后我会优化下写个扩展中间件。查看下具体的情况携带上token以后发起请求无论是自定义固定的参数还是Claims中的变量都传给了下游服务并且下游的Response的Header也有了值。好啦网关系列的分享就先到这里了咱们下次再见说说注册中心集成功能。