dubbo 安全方案

2019-11-05  本文已影响0人  乘以零

如果dubbo服务必须开放在外网上 如何控制权限验证呢?想了下有一下集中方案

1.开启linux 防火墙 ,只让白名单中的ip访问固定的端口(或禁止黑名单访问)

白名单

iptables -A INPUT -s 192.168.0.0/8 -p tcp --dport 22 -j ACCEPT

黑名单

iptables -A INPUT -s 192.168.0.0/8 -p tcp --dport 22 -j DROP
# or
iptables -A INPUT -s 192.168.0.0/8 -p tcp --dport 22 -j REJECT

2.dubbo 的 token 机制

http://dubbo.apache.org/zh-cn/docs/user/demos/token-authorization.html

<dubbo:provider interface="com.foo.BarService" token="true" />

这种方法,防止直连dubbo,只能通过注册中心的方式,然后把注册中心的ip或端口号禁用掉。
但如果禁用掉了注册中心,服务又不能通过直连的方式调用,那这个服务在外网和内网有区别么???
如果不禁用注册中心的端口,采用注册中心用户名密码制

<dubbo:registry username="" password="" />

但是,我们大部分使用的注册中心为zookeeper,zookeeper用的ACL机制,子节点不能继承父节点的权限,所以,当注册中心使用zk时候,这个方法不可行。可以换成别的注册中心。

3.采用nginx代理

原dubbo的端口20880,代理后的端口为20881,然后把20880防火墙禁止外网访问,nginx加上权限校验。
shell 创建用户名 密码

  > cd /etc/nginx/
  > htpasswd -c -d /etc/nginx/passwd.db  username
  > password

nginx.conf

stream{
    upstream dubbo{
        server 127.0.0.1:20880;
    }
    server{
        auth_basic "User Authentication";
        auth_basic_user_file /etc/nginx/passwd.db;
        listen 20881;
        proxy_pass dubbo;
    }
}

4.采用dubbo filter机制,这样每个工程都要加上相应的filter

provider 加

@Activate(group = Constants.PROVIDER)
public class AuthProviderFilter implements Filter {
    //这个用户名 密码可以写在配置文件中
    private String username = "abcde";
    private String password = "12345";

    @Override
    public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
        RpcContext.getContext().setAttachment("auth.username", username);
        RpcContext.getContext().setAttachment("auth.password", username);

        Result result = invoker.invoke(invocation);
        return result;
    }
}

consumer 加

@Activate(group = Constants.CONSUMER)
public class AuthConsumerFilter implements Filter {
    //这个用户名 密码可以写在配置文件中
    private String username = "abcde";
    private String password = "12345";

    @Override
    public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
        String u = RpcContext.getContext().getAttachment("username");
        String p = RpcContext.getContext().getAttachment("password");

        //认证成功
        if (username.equals(u) && password.equals(p)) {
            Result result = invoker.invoke(invocation);
            return result;
        }
        // 认证失败 报错
        throw new RpcException("auth error");
    }
}

也可以合在一起

@Activate(group = { Constants.CONSUMER, Constants.PROVIDER })
public class AuthFilter implements Filter {
    //这个用户名 密码可以写在配置文件中
    private String username = "abcde";
    private String password = "12345";

    @Override
    public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
        //如果是消费端
        if (RpcContext.getContext().isConsumerSide()) {
            String u = RpcContext.getContext().getAttachment("username");
            String p = RpcContext.getContext().getAttachment("password");

            //认证成功
            if (username.equals(u) && password.equals(p)) {
                Result result = invoker.invoke(invocation);
                return result;
            }
        } else {
            RpcContext.getContext().setAttachment("auth.username", username);
            RpcContext.getContext().setAttachment("auth.password", username);
            Result result = invoker.invoke(invocation);
            return result;
        }
        throw new RpcException("auth error");
    }
}

上一篇下一篇

猜你喜欢

热点阅读