pulsar自定义创建发布和订阅主题权限插件开发

pulsar自定义创建发布和订阅主题权限插件开发

pulsar自定义认证插件开发

Linux MacBook单机部署Pulsar并开启认证功能

pulsar集群搭建_亲测成功

pulsar的权限分为两部分:

  • 1 客户端连接时认证的权限 (前面的博客讲解了)

  • 2 客户端创建,发送和订阅主题的权限

pulsar开启权限功能

默认情况下,pulsar是不会开启客户端连接认证和主题的权限控制,即客户端到broker之间、broker到broker之间的访问都没有任何限制。但是在线上环境中,对于权限的控制往往是很重要的。连接认证和主题的权限是两个独立的功能,(连接认证前面博客已开启)可以分别开启

如果要开启对权限的控制,首先需要打开对连接的认证。

修改配置,开启主题权限功能
standalone.conf 或 broker.conf

#开启权限
authorizationEnabled=true

# 自定义权限类,这个类需要我们自己实现,继承org.apache.pulsar.broker.authorization.AuthorizationProvider接口即可
authorizationProvider=com.beyond.authz.PulsarAuthorizationProviderMysql

#配置超级权限
superUserRoles=admin,test

自定义权限插件开发

pulsar提供了AuthorizationProvider接口。我们自定义权限实现接口

权限实现相关代码

package com.beyond.authz;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import org.apache.pulsar.broker.ServiceConfiguration;
import org.apache.pulsar.broker.authentication.AuthenticationDataSource;
import org.apache.pulsar.broker.authorization.AuthorizationProvider;
import org.apache.pulsar.broker.cache.ConfigurationCacheService;
import org.apache.pulsar.broker.resources.PulsarResources;
import org.apache.pulsar.common.naming.NamespaceName;
import org.apache.pulsar.common.naming.TopicName;
import org.apache.pulsar.common.policies.data.*;
import org.apache.pulsar.common.util.FutureUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.util.Set;
import java.util.concurrent.CompletableFuture;

public class PulsarAuthorizationProviderMysql implements AuthorizationProvider  {

    private static final Logger log = LoggerFactory.getLogger(PulsarAuthorizationProviderMysql.class);

    public ServiceConfiguration conf;

    private PulsarResources pulsarResources;

    @Override
    public void initialize(ServiceConfiguration conf, ConfigurationCacheService configCache) throws IOException {
        log.info("PulsarAuthorizationProviderMysql.initialize*******************************");
        this.conf = conf;
        this.pulsarResources = configCache.getPulsarResources();
    }

    @Override
    public CompletableFuture<Boolean> canProduceAsync(TopicName topicName, String role, AuthenticationDataSource authenticationData) {
        log.info("PulsarAuthorizationProviderMysql.canProduceAsync***************************topicname:{}, role={}", topicName.toString(), role);
        return mysqlQuery(topicName, role);
    }

    private CompletableFuture<Boolean> mysqlQuery(TopicName topicName, String role) {
	//查询数据库,看有没有权限
        String aaa = MysqlConfig.queryAuthByTenantAndnamespace("select aaa from test where bbb = ? and tenant = ? and namespace = ?", role, topicName.getTenant(), topicName.getNamespacePortion());
        log.info("mysql queryAuthByTenantAndnamespace ********{}, {}, {}, {}", role, topicName.getTenant(), topicName.getNamespacePortion(), aaa);
        CompletableFuture<Boolean> permissionFuture = new CompletableFuture<>();
        if (aaa != null) {
            permissionFuture.complete(true);
        } else {
            log.error("mysqlQuery****{},{}**********result*****{}", topicName.toString(), role, aaa);
            permissionFuture.complete(false);
        }
        return permissionFuture;
    }

    @Override
    public CompletableFuture<Boolean> canConsumeAsync(TopicName topicName, String role, AuthenticationDataSource authenticationData, String subscription) {
        log.info("PulsarAuthorizationProviderMysql.canConsumeAsync***************************topicname:{}, role={}", topicName.toString(), role);
        return mysqlQuery(topicName, role);
    }

    @Override
    public CompletableFuture<Boolean> canLookupAsync(TopicName topicName, String role, AuthenticationDataSource authenticationData) {
        log.info("PulsarAuthorizationProviderMysql.canLookupAsync***************************topicname:{}, role={}", topicName.toString(), role);
        return mysqlQuery(topicName, role);
    }

    @Override
    public CompletableFuture<Boolean> allowFunctionOpsAsync(NamespaceName namespaceName, String role, AuthenticationDataSource authenticationData) {
        return defaultResult(true, "allowFunctionOpsAsync", namespaceName, role);
    }

    @Override
    public CompletableFuture<Boolean> allowSourceOpsAsync(NamespaceName namespaceName, String role, AuthenticationDataSource authenticationData) {
        return defaultResult(true, "allowSourceOpsAsync", namespaceName, role);
    }

    @Override
    public CompletableFuture<Boolean> allowSinkOpsAsync(NamespaceName namespaceName, String role, AuthenticationDataSource authenticationData) {
        return defaultResult(true, "allowSinkOpsAsync", namespaceName, role);
    }

    @Override
    public CompletableFuture<Void> grantPermissionAsync(NamespaceName namespace, Set<AuthAction> actions, String role, String authDataJson) {
        return defaultVoidResult("grantPermissionAsync", namespace, role);
    }

    @Override
    public CompletableFuture<Void> grantSubscriptionPermissionAsync(NamespaceName namespace, String subscriptionName, Set<String> roles, String authDataJson) {
        return defaultVoidResult("grantSubscriptionPermissionAsync", namespace, roles.toString());
    }

    @Override
    public CompletableFuture<Void> revokeSubscriptionPermissionAsync(NamespaceName namespace, String subscriptionName, String role, String authDataJson) {
        return defaultVoidResult("revokeSubscriptionPermissionAsync", namespace, role);
    }

    @Override
    public CompletableFuture<Void> grantPermissionAsync(TopicName topicName, Set<AuthAction> actions, String role, String authDataJson) {
        return defaultVoidResult("grantPermissionAsync", topicName, role);
    }

    @Override
    public void close() {
        log.info("PulsarAuthorizationProviderMysql.close********************************");
    }

    @Override
    public CompletableFuture<Boolean> allowTenantOperationAsync(String tenantName, String role, TenantOperation operation, AuthenticationDataSource authData) {
        log.info("PulsarAuthorizationProviderMysql.allowTenantOperationAsync********************************tenantName={}, role={}", tenantName, role);
        CompletableFuture<Boolean> permissionFuture = new CompletableFuture<>();
        permissionFuture.complete(true);
        return permissionFuture;
    }

    @Override
    public CompletableFuture<Boolean> allowNamespaceOperationAsync(NamespaceName namespaceName, String role, NamespaceOperation operation, AuthenticationDataSource authData) {
        return defaultResult(true, "allowNamespaceOperationAsync", namespaceName, role);
    }

    @Override
    public CompletableFuture<Boolean> allowNamespacePolicyOperationAsync(NamespaceName namespaceName, PolicyName policy, PolicyOperation operation, String role, AuthenticationDataSource authData) {
        return defaultResult(true, "allowNamespacePolicyOperationAsync", namespaceName, role);
    }

    @Override
    public CompletableFuture<Boolean> allowTopicOperationAsync(TopicName topic, String role, TopicOperation operation, AuthenticationDataSource authData) {
        log.info("PulsarAuthorizationProviderMysql.allowTopicOperationAsync********************************topicName={}, role={}, topicOperation={}",
                topic.toString(), role, operation.toString());

        CompletableFuture<Boolean> isAuthorizedFuture;

        switch (operation) {
            case LOOKUP:
            case GET_STATS:
                return canLookupAsync(topic, role, authData);
            case PRODUCE:
                return canProduceAsync(topic, role, authData);
            case GET_SUBSCRIPTIONS:
            case CONSUME:
            case SUBSCRIBE:
            case UNSUBSCRIBE:
            case SKIP:
            case EXPIRE_MESSAGES:
            case PEEK_MESSAGES:
            case RESET_CURSOR:
                return canConsumeAsync(topic, role, authData, authData.getSubscription());
            case TERMINATE:
            case COMPACT:
            case OFFLOAD:
            case UNLOAD:
            case ADD_BUNDLE_RANGE:
            case GET_BUNDLE_RANGE:
            case DELETE_BUNDLE_RANGE:
                return validateTenantAdminAccess(topic.getTenant(), role, authData);
            default:
                return FutureUtil.failedFuture(
                        new IllegalStateException("TopicOperation [" + operation.name() + "] is not supported."));
        }
    }

    public CompletableFuture<Boolean> validateTenantAdminAccess(String tenantName,
                                                                String role,
                                                                AuthenticationDataSource authData) {
        return isSuperUser(role, authData, conf)
                .thenCompose(isSuperUser -> {
                    if (isSuperUser) {
                        return CompletableFuture.completedFuture(true);
                    } else {
                        return CompletableFuture.completedFuture(false);
                    }
                });
    }

    public boolean isSuper(String role, AuthenticationDataSource authData) {
        //超级管理员
        CompletableFuture<Boolean> isSuccess = isSuperUser(role, authData, conf)
                .thenCompose(isSuperUser -> {
                    if (isSuperUser) {
                        return CompletableFuture.completedFuture(true);
                    } else {
                        return CompletableFuture.completedFuture(false);
                    }
                });
        try {
            return isSuccess.get();
        } catch (Exception e) {
            e.printStackTrace();
            log.error("isSuper***********error:{}", e.getMessage());
        }
        return false;
    }

    @Override
    public CompletableFuture<Boolean> allowTopicPolicyOperationAsync(TopicName topic, String role, PolicyName policy, PolicyOperation operation, AuthenticationDataSource authData) {
        return defaultResult(true, "allowTopicPolicyOperationAsync", topic, role);
    }

    CompletableFuture<Boolean> defaultResult(boolean isPermission, String name, TopicName topicName, String role) {
        log.info("PulsarAuthorizationProviderMysql.defaultResult*******************************func name={},topicname:{}, role={}", name, topicName.toString(), role);
        CompletableFuture<Boolean> permissionFuture = new CompletableFuture<>();
        permissionFuture.complete(isPermission);
        return permissionFuture;
    }

    CompletableFuture<Boolean> defaultResult(boolean isPermission, String name, NamespaceName namespaceName, String role) {
        log.info("PulsarAuthorizationProviderMysql.defaultResult*******************************func name={},namespacename:{}, role={}", name, namespaceName.toString(), role);
        CompletableFuture<Boolean> permissionFuture = new CompletableFuture<>();
        //true有权限
        permissionFuture.complete(isPermission);
        return permissionFuture;
    }

    CompletableFuture<Void> defaultVoidResult(String name, TopicName topicName, String role) {
        log.info("PulsarAuthorizationProviderMysql.defaultVoidResult*******************************func name={},topicname:{}, role={}", name, topicName.toString(), role);
        CompletableFuture<Void> permissionFuture = new CompletableFuture<>();
        permissionFuture.complete(null);
        return permissionFuture;
    }

    CompletableFuture<Void> defaultVoidResult(String name, NamespaceName namespaceName, String role) {
        log.info("PulsarAuthorizationProviderMysql.defaultVoidResult*******************************func name={},namespacename:{}, role={}", name, namespaceName.toString(), role);
        CompletableFuture<Void> permissionFuture = new CompletableFuture<>();
        permissionFuture.complete(null);
        return permissionFuture;
    }

}


maven引入相关依赖

       		 <dependency>
			<groupId>com.alibabagroupId>
			<artifactId>druidartifactId>
			<version>1.1.22version>
		dependency>
		<dependency>
			<groupId>mysqlgroupId>
			<artifactId>mysql-connector-javaartifactId>
			<scope>runtimescope>
		dependency>
		<dependency>
			<groupId>org.apache.pulsargroupId>
			<artifactId>pulsar-brokerartifactId>
			<version>2.8.0version>
		dependency>
		<dependency>
			<groupId>com.alibabagroupId>
			<artifactId>fastjsonartifactId>
			<version>1.2.62version>
		dependency>

打包部署

编写好代码后用maven命令打包:
mvn clean install -Dmaven.test.skip=true

然后把打好的jar包放到pulsar的lib目录下,如:apache-pulsar-2.8.1/lib,
重启broker组件即可生效。

测试自定义权限功能

package com.beyond.auth;

import org.apache.pulsar.client.api.*;

public class TestPulsarAuth {

    public static void main(String[] args) throws Exception {

        PulsarClient client = PulsarClient.builder()
                .serviceUrl("pulsar://127.0.0.1:6650")
		//admin有全部权限,配置一个没有权限的角色放数据库测试
                .authentication("com.beyond.auth.AuthenticationMysql", "e3592948c:admin:123456")
                .build();

        //订阅
        ConsumerThreadAuth consumerThread = new ConsumerThreadAuth(client);
        consumerThread.start();

        //发布
        Producer<String> stringProducer = client.newProducer(Schema.STRING)
                .topic("persistent://my-tenant/my-namespace/aaa")
                .create();
        for (int i = 0; i < 100; i++) {
            Thread.sleep(1000);
            stringProducer.send(i + "aaaaaaaa111112222333");
        }


    }

}

class ConsumerThreadAuth extends Thread {

    private PulsarClient client;

    public ConsumerThreadAuth(PulsarClient client) throws Exception {
        this.client = client;
    }

    @Override
    public void run()  {
        try {
            Consumer consumer = client.newConsumer()
                    .topicsPattern("persistent://my-tenant/my-namespace/aaa")
                    .subscriptionName("my-subscription")
                    .subscriptionType(SubscriptionType.Shared)
                    .subscribe();


            while (true) {
                // Wait for a message
                Message msg = consumer.receive();

                try {
                    // Do something with the message
                    System.out.println(msg.getTopicName() + "222222Message received: " + new String(msg.getData()));

                    // Acknowledge the message so that it can be deleted by the message broker
                    consumer.acknowledge(msg);
                } catch (Exception e) {
                    // Message failed to process, redeliver later
                    consumer.negativeAcknowledge(msg);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

    }
}

权限控制原理

pulsar的权限是在role的层次上进行控制的。在客户端连接认证成功后,会保存客户端的role,然后根据不同的接口结合role和具体操作进行权限限制。

比如:数据库对每个role进行权限管理,当客户端连接pulsar时,把用户名当成role,连接成功后pular会记录此客户端的用户名,当客户端发起订阅和发布主题时,会调用对应的权限实现接口里对应的方法,只要拿到用户名role和主题去数据库查询是否有权限即可.

参考链接:
https://blog.csdn.net/casuallc/article/details/119151911

你可能感兴趣的:(mq,mq,pulsar,mqtt)