ranger插件开发(下)

要在ranger中支持一个新的服务模块的权限校验,可以分为两部分,一部分是在ranger中添加一个服务模块,然后添加该服务的实例并配置对应的权限策略;另一部分就是在真正的服务端开发插件,从ranger中拉取权限策略,并提供接口完成真正的鉴权动作。

本文就来聊一聊相关的知识。

在ranger中添加服务模块

在ranger中添加服务模块可以分为3个步骤:

  1. 编写服务定义配置文件

具体怎么编写这个配置文件,可以参考上篇文章,里面详细介绍了配置文件中的各个字段。

配置文件通常命名为"ranger-servicedef-xxx.json",其中xxx为服务的名字,即和配置文件中的name字段的值保持一致。

另外,该文件需存放到agents-common/src/main/resources/service-defs目录下。

  1. 编写ranger中的服务实现类

还记得在服务定义配置文件中有个字段为implClass吗,它的值为类的名称。

例如:org.apache.ranger.services.rabbitmq.RangerServiceRabbitmq

每个服务模块都需要有一个对应的实现类。这个实现类通常继承自抽象类RangerBaseService,在构造函数和init方法中,一般都是调用父类的实现。

而两个需要自行实现的方法为:

// 实现对配置文件d的有效性j检查

public abstract Map<String, Object> validateConfig() throws Exception;

// 实现对资源的检索

public abstract List<String> lookupResource(ResourceLookupContext context) throws Exception;

可以根据实际需要完成具体的实现。当然,也可以返回一个空的map或list,即不进行有效性检查和不支持检索。

另外,还有一个可以重写的方法是:

public List<RangerPolicy> getDefaultRangerPolicies() throws Exception

该方法在父类实现中提供了默认的策略。即一旦在ranger的web界面中添加了该服务的一个实例,那么会自动添加默认的策略。

因此,可以在具体实现类中根据需要改写该方法的实现。

  1. 加载服务模块

完成前面两步后,在ranger中添加服务的主要工作基本完成,剩下的就是启动初始化加载该服务模块(实例化具体的类对象)使其可以在界面中展示了。

初始化动作是在EmbeddedServiceDefsUtil类中完成的,具体包括:

  • 在静态变量DEFAULT_BOOTSTRAP_SERVICEDEF_LIST中添加服务名

    注意:名称要和服务定义配置文件中name的值完全匹配。

  • 添加类成员,并在init方法中完成初始实例化动作。

简单示例代码如下:

public static final string RABBITMQ_IMPL_CLASS_NAME = "rabbitmq";

private RangerServiceDef rabbitmqServiceDef;

public void init(ServiceStore store) {

    ...

    rabbitmqServiceDef = getOrCreateServiceDef(store, RABBITMQ_IMPL_CLASS_NAME);

}

完成上述步骤后,重新编译ranger,然后安装部署启动ranger,在ranger的web界面,就可以看到我们添加的服务模块了。

ranger插件开发(下)

除了上面的方法,在官网的rest接口文档中,看到还可以通过rest接口添加服务模块。

ranger插件开发(下)

实测了一下,确实可以成功添加,在ranger的web控制台上也能看到添加的服务模块。但是有一点需要注意:由于服务定义配置文件中需要指明对应的实现类,而如果ranger内部找不到对应的实现类,虽然可以成功添加服务模块,但是在添加服务模块的具体实例时,会因为没有对应的实现类而报错!

ranger插件开发(下)

一种可行的解决办法是:

仍旧需要完成对应类的实现,并制作相应的jar包,然后将jar包放到ranger服务对应的插件目录中,如下图所示:

ranger插件开发(下)

此后,再通过rest接口添加服务模块,就不会报错了。

插件开发

完成了在ranger中添加模块后,接下来的工作就是编写插件,并嵌入到具体服务中,完成具体的鉴权动作了。

实际上,ranger已经提供了完备的框架,我们只需要简单开发两三个继承类,最终调用指定接口就可以完成整个插件开发了。

在进行开发之前,有必要先了解ranger插件框架中几个核心的类。

  • RangerBasePlugin

    ranger插件中最核心的类,负责从ranger拉取策略并缓存,同时对外暴露接口完成资源访问的权限校验。

  • RangerAccessResourceImpl

    资源的封装类,调用鉴权接口时,需要构造这么一个类,并设置需要鉴权的资源。

  • RangerAccessRequestImpl

    资源访问的封装类,内部包含了上面提到的资源封装类,同时还包括访问资源的用户、用户组、角色、访问的类型、客户端IP等信息。最终调用接口进行权限校验时,该类的实例对象需要作为参数被传入。

  • RangerDefaultAuditHandler

    审计日志的处理类,所有的权限校验动作都可以作为审计日志被记录下来

有了上面的大概认识后,对于插件开发,我们只需要做这么几个动作:

  1. 编写一个继承RangerBasePlugin的类

通常,只需要实现构造函数和init方法即可。

在构造函数中,会调用父类的构造函数,这里需要正确传入服务的类型,也就是服务定义配置文件中name字段的值。

另外就是,在init方法中,调用父类的init方法,这个时候在插件内部会触发启动线程,向ranger注册并定时从ranger拉取策略。

  1. 编写粘合类

这是一个承上启下的类,也就是在具体服务内部先调用该类完成初始化动作,此后调用该类的接口进行权限校验。

而在这个类的内部,将上面RangerBasePlugin的继承类对象作为类成员,初始化时调用RangerBasePlugin继承类对象的init方法,完成插件的初始化动作。

而对外提供的的鉴权接口中,则对需要权限校验的资源、访问动作、访问的用户、用户组等信息封装成RangerAccessRequestImpl类对象,最后调用RangerBasePlugin继承类对象的isAccessAllowed方法,完成权限校验动作。

  1. 编写一个继承RangerDefaultAuditHandler的类(可选)

一个简单的示例代码:

public class RangerRabbitmqAuthorizer {

    private static volatile RangerRabbitmqPlugin rmqPlugin = null;

    public RangerRabbitmqAuthorizer () {}

    public void init() {

        RangerRabbitmqPlugin plugin = rmqPlugin;

        if(plugin == null) {

            synchronized(RangerRabbitmqAuthorizer.class) {

                plugin = rmqPlugin;

                if(plugin==null) {

                    plugin = new RangerRabbitmqPlugin();

                    plugin.init();

                    rmqPlugin = plugin;

                }

            }

        }

    }

    public boolean checkPermission(String userName, String userGroups, string clientIp, String a) {
        Date eventTime = new Date();

        RangerAccessRequestImpl rangerRequest = new RangerAccessRequestImpl();

        rangerRequest.setUser(userName);

        rangerRequest.setUserGroups(Sets.newHashSet(userGroups));

        rangerRequest.setClientIPAddress(clientIp);

        rangerRequest.setAccessTime(eventTime);

        String action = accessType;

        RangerAccessResourceImpl rangerResource = new RangerAccessResourceImpl();

        rangerRequest.setResource(rangerResource);

        rangerRequest.setAccessType(accessType);

        rangerRequest.setAction(action);

        rangerRequest.setRequestData(resourceName);

        rangerResource.setValue(resourceType, resourceName);

        boolean retValue = false;

        try {

            RangerAccessResult result = rmqPlugin.isAccessAllowed(rangerRequest);

            if(result == null) {

                // Ranger plugin return null, returning false

            } else {

                retValue = result.getIsAllowed();

            }

        } catch (Throwable t) {

        } finally {

        }

        return retValue;

    }

    public static void main(String[] args) {

        RangerRabbitmqAuthorizer rmqAuthorizer = new RangerRabbitmqAuthorizer();

        rmqAuthorizer.init();

        Boolean isAllowed = rmqAuthorizer.checkPermission(

            "hncscwc",  // 资源访问的用户名

            "hncscwc",  // 资源访问的用户组

            "127.0.0.1",  // 资源访问的客户端IP

            "consume",  // 资源访问的类型(对队列j进行消费)

            "queue",  // 资源访问的类型

            "test"  // 具体访问的队列名称

    }

}

class RangerRabbitmqPlugin extends RangerBasePlugin {

    public RangerRabbitmqPlugin() {

        super("rabbitmq", "rabbitmq");

    }

    public void init() {

        super.init();

        RangerDefaultAuditHandler auditHandler = new RangerDefaultAuditHandler(getConfig());
    }
        super.setResultProcessor(auditHandler);

    }

}

来小结一下:

本文主要介绍了在ranger中支持一个新服务(插件开发)权限校验的步骤和流程,最后也给出了简单示例代码。未提到的一部分是整个工程的设置、编译打包的一些细节,还包括插件端配置文件的一些内容,这一块相对比较简单,也可以直接参考源码中其他模块的实现。

本文转载自:https://www.modb.pro/db/131554

0 0 投票数
文章评分

本文转载自 hncscwc,原文链接:https://www.modb.pro/db/131554。

(0)
上一篇 2022-06-10 20:13
下一篇 2022-06-11 12:34

相关推荐

订阅评论
提醒
guest
0 评论
内联反馈
查看所有评论
0
希望看到您的想法,请您发表评论x