dubbo解析-详解令牌验证原理
本文基于dubbo 2.7.5版本代码
dubbo提供了令牌验证功能。下面是官网对令牌的介绍:
通过令牌验证在注册中心控制权限,以决定要不要下发令牌给消费者,可以防止消费者绕过注册中心访问提供者,另外通过注册中心可灵活改变授权方式,而不需修改或升级提供者。
从这段介绍可以看出,令牌要求消费端必须通过注册中心获取令牌,否则无法访问服务。这样注册中心就有了管理令牌的功能,注册中心可以确定哪些消费者获得令牌,哪些消费者无法获得令牌,还可以使令牌失效。下面以zookeeper为例,简单介绍一下如何实现注册中心的管理功能。
-
服务端启动的时候会监听注册中心的“/dubbo/接口名/configurators”节点,该节点可以存储服务端的配置信息,如果需要修改令牌,可以修改该节点数据。修改后,服务端的监听器OverrideListener收到修改后的配置,服务端会重新发布服务,并且重新注册信息。 消费端启动的时候会监听注册中心的“/dubbo/接口名/providers”节点,该节点存储服务提供者的信息,当提供者启动完毕或者重新发布服务,都会修改该节点。消费端监听到该监听变化后,通知监听器ReferenceConfigurationListener,该监听器会重新创建与服务端的连接,重新注册消费者信息。
那么我们怎么配置令牌?
一、配置令牌
配置令牌可以只使单个服务生效,也可以使服务端所有的服务都生效。
-
@Service(token=“true”)或者@Service(token=“123456”),前者dubbo生成随机token,后者指定了token为“123456”; dubbo.provider.token="true"或者dubbo.provider.token=“123456”。可以使服务端所有的服务都生效。
二、原理
1、服务端令牌的生成
服务端启动时,dubbo调用ServiceConfig暴露服务会执行如下代码:
if (!ConfigUtils.isEmpty(token)) { if (ConfigUtils.isDefault(token)) { map.put(TOKEN_KEY, UUID.randomUUID().toString()); } else { map.put(TOKEN_KEY, token); } }
从上面这段代码可以看出,如果配置了token,则使用配置值,如果没有配置,则使用随机UUID值。token值放入map中后,dubbo将其发布到注册中心providers节点下。这样消费端就可以获取到令牌了。
2、消费端保存令牌
消费端启动后,从providers节点下得到服务提供者配置信息,其中就包含了token值,消费端将token值保存在本地。每次访问服务时,将该值放在报文中发送过去。比如使用dubbo协议的话,token放在attachments里面。
3、令牌验证
服务端配置了token值后,dubbo会引入过滤器TokenFilter校验token。
//从group值可以看出,该过滤器只在服务端生效 @Activate(group = CommonConstants.PROVIDER, value = TOKEN_KEY) public class TokenFilter implements Filter { @Override public Result invoke(Invoker<?> invoker, Invocation inv) throws RpcException { //获取服务端配置的token值 String token = invoker.getUrl().getParameter(TOKEN_KEY); if (ConfigUtils.isNotEmpty(token)) { Class<?> serviceType = invoker.getInterface(); Map<String, String> attachments = inv.getAttachments(); //从请求数据里面得到token,也就是消费端发送过来的token String remoteToken = (attachments == null ? null : attachments.get(TOKEN_KEY)); //两个token值如果不一致就抛出异常 if (!token.equals(remoteToken)) { throw new RpcException("Invalid token! Forbid invoke remote service " + serviceType + " method " + inv.getMethodName() + "() from consumer " + RpcContext.getContext().getRemoteHost() + " to provider " + RpcContext.getContext().getLocalHost()); } } return invoker.invoke(inv); } }
上一篇:
IDEA上Java项目控制台中文乱码