開發與維運

Spring Boot、 Spring Cloud 基於Filter 的Dubbo服務權限控制

1,首先引入依賴

<dependency>
    <groupId>org.apache.dubbo</groupId>
    <artifactId>dubbo</artifactId>
</dependency>

2,編寫代碼

package org.jeckxu.magical.core.dubbo.interceptor.props;

import com.google.common.collect.Maps;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

import java.io.Serializable;
import java.util.List;
import java.util.Map;

/**
 * @author jeckxu
 */
@Data
@Component(DubboAuthProperties.BEAN_NAME)
@ConfigurationProperties(DubboAuthProperties.PREFIX)
public class DubboAuthProperties implements Serializable {

    public static final String PREFIX = "magical.dubbo.auth";

    public static final String BEAN_NAME = "dubboAuthProperties";

    /**
     * 是否開啟Dubbo 服務鑑權:默認為:false
     */
    private Boolean enabled = Boolean.FALSE;

    private Map<String, List<String>> appIdAllowedOfPath = Maps.newHashMap();
}
package org.jeckxu.magical.core.dubbo.interceptor.constants;

/**
 * @author jeckxu
 */
public interface DubboConstants {

    String PROVIDER = "provider";

    String CONSUMER = "consumer";

    String TRACE_ID = "traceId";
}
package org.jeckxu.magical.core.dubbo.interceptor;

import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.common.extension.Activate;
import org.apache.dubbo.rpc.*;
import org.jeckxu.magical.core.dubbo.interceptor.constants.DubboConstants;
import org.jeckxu.magical.core.tool.api.R;
import org.jeckxu.magical.core.tool.utils.StringUtil;
import org.springframework.boot.system.SystemProperties;

/**
 * @author jeckxu
 */
@Slf4j
@Activate(group = {DubboConstants.CONSUMER})
public class DubboConsumerAuthFilter implements Filter {
    @Override
    public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
        String appId = SystemProperties.get("spring.application.name");
        String currentPath = invoker.getUrl().getPath().concat("." + invocation.getMethodName());
        if (StringUtil.isNotBlank(appId)) {
            RpcContext.getContext().setAttachment("app_id", appId);
            return invoker.invoke(invocation);
        } else {
            log.error(" [DUBBO] Consumer 獲取服務名稱失敗,Dubbo Consumer 停止調用!Path:".concat(currentPath));
            return AsyncRpcResult.newDefaultAsyncResult(
                R.fail("Consumer 獲取服務名稱失敗,Dubbo Consumer 停止調用!Path:" + currentPath), invocation);
        }
    }
}
package org.jeckxu.magical.core.dubbo.interceptor;

import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.common.extension.Activate;
import org.apache.dubbo.common.extension.ExtensionFactory;
import org.apache.dubbo.common.extension.ExtensionLoader;
import org.apache.dubbo.rpc.*;
import org.jeckxu.magical.core.dubbo.interceptor.constants.DubboConstants;
import org.jeckxu.magical.core.dubbo.interceptor.props.DubboAuthProperties;
import org.jeckxu.magical.core.tool.api.R;

import java.util.List;


/**
 * @author jeckxu
 */
@Slf4j
@Activate(group = {DubboConstants.PROVIDER})
public class DubboProviderAuthFilter implements Filter {

    ExtensionFactory objectFactory =
        ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension();

    private final DubboAuthProperties dubboAuthProperties
        = objectFactory.getExtension(DubboAuthProperties.class, DubboAuthProperties.BEAN_NAME);


    @Override
    public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
        if (dubboAuthProperties.getEnabled()) {
            String appId = RpcContext.getContext().getAttachment("app_id");
            String currentPath = invoker.getUrl().getPath().concat("." + invocation.getMethodName());
            List<String> currentPathAllowedAppIds = dubboAuthProperties.getAppIdAllowedOfPath().get(currentPath);
            if (null == currentPathAllowedAppIds) {
                log.warn(" [DUBBO] Provider 該接口無權限配置,請留意!Path:{},AppId:{}", currentPath, appId);
                return invoker.invoke(invocation);
            }
            if (currentPathAllowedAppIds.contains(appId)) {
                return invoker.invoke(invocation);
            } else {
                log.error(" [DUBBO] Provider 接口鑑權失敗,無法提供服務!Path:{},AppId:{}", currentPath, appId);
                return AsyncRpcResult.newDefaultAsyncResult(R.fail("Provider 接口鑑權失敗,無法提供服務!Path:"
                    + currentPath + ",AppId:" + appId), invocation);
            }
        } else {
            return invoker.invoke(invocation);
        }
    }
}

以上就是基於Filter 實現的Dubbo服務權限控制代碼了,現在我們在Dubbo 服務的Provider端編寫配置文件。

magical:
  dubbo:
    auth:
      enabled: true
      app-id-allowed-of-path:
        org.jeckxu.magical.system.user.rpc.AuthRpc.adminLogin:
          - aaaaa

現在,只有AppId 為 aaaaa 的 Dubbo Consumer 端才可以調用 org.jeckxu.magical.system.user.rpc.AuthRpc.adminLogin 服務。

配合Spring Cloud Alibaba Nacos組件可以實現動態配置。

Leave a Reply

Your email address will not be published. Required fields are marked *