Dubbo过滤器 - TokenFilter

概述

Dubbo 框架支持通过令牌验证在注册中心控制权限,以决定是否下发令牌给消费者,以防止消费者绕过注册中心访问服务提供者,另外通过注册中心可灵活改变授权方式,而不需修改或升级提供者。

配置

  • 全局开启令牌
    1
    2
    3
    4
    <!--随机token令牌,使用UUID生成-->
    <dubbo:provider interface="com.foo.BarService" token="true" />
    <!--固定token令牌,相当于密码-->
    <dubbo:provider interface="com.foo.BarService" token="123456" />
  • 服务级别开启令牌
    1
    2
    3
    4
    <!--随机token令牌,使用UUID生成-->
    <dubbo:service interface="com.foo.BarService" token="true" />
    <!--固定token令牌,相当于密码-->
    <dubbo:service interface="com.foo.BarService" token="123456" />

Token 流转

服务提供端

1
2
3
4
5
6
7
8
9
10
+--- ServiceConfig#doExportUrlsFor1Protocol
// token 【使暴露出去的服务更安全,使用token做安全校验】
if (!ConfigUtils.isEmpty(token)) {
// true || default 时,UUID 随机生成
if (ConfigUtils.isDefault(token)) {
map.put(Constants.TOKEN_KEY, UUID.randomUUID().toString());
} else {
map.put(Constants.TOKEN_KEY, token);
}
}

服务提供者将设置的 Token 最终会设置到 URL 中,最终写入到注册中心上。

服务消费端

服务消费者从注册中心获取服务提供者的 URL ,从而获得该服务的 Token 。在创建 RpcInvocation 时会自动带上 Token,最终会将 Token 发送到服务提供方,而 TokenFilter 就是处理来自服务消费方的 Token 的。

TokenFilter

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
@Activate(group = Constants.PROVIDER, value = Constants.TOKEN_KEY)
public class TokenFilter implements Filter {
/**
* 对请求的令牌做校验
*
* @param invoker service
* @param inv
* @return
* @throws RpcException
*/
@Override
public Result invoke(Invoker<?> invoker, Invocation inv) throws RpcException {
// 获得服务提供者配置的Token 值
String token = invoker.getUrl().getParameter(Constants.TOKEN_KEY);
if (ConfigUtils.isNotEmpty(token)) {
Class<?> serviceType = invoker.getInterface();
// 从 RpcInvocation 的 隐式参数中,获得 Token 值
Map<String, String> attachments = inv.getAttachments();
String remoteToken = attachments == null ? null : attachments.get(Constants.TOKEN_KEY);

// 验证消费方【RpcInvocation的信息】传过来的的Token 和 服务提供者配置的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);
}
}