基于 Spring Boot + Nacos + Dubbo 的完整自定义鉴权过滤器实现方案

实现说明

  1. 整体流程

    • 服务提供者启动时会注册到 Nacos,并启用自定义的AuthFilter
    • 客户端调用服务前,通过RpcContext设置鉴权信息(token 和应用名)
    • 服务端的AuthFilter会拦截请求,验证鉴权信息
    • 验证通过则继续调用,否则抛出异常
  2. 关键实现点

    • 使用@Activate注解指定过滤器在服务端生效
    • 通过 SPI 机制注册过滤器
    • invoke方法中实现鉴权逻辑
    • 利用RpcContext传递和获取调用上下文信息
  3. 动态配置

    • 进阶实现中添加了 Nacos 配置中心客户端
    • 可以动态获取和更新鉴权规则,无需重启服务

使用时,需要先启动 Nacos 服务器,然后分别启动服务提供者和消费者,即可看到鉴权效果。你可以根据实际业务需求修改validateTokenhasMethodPermission方法中的鉴权逻辑。

1.自定义鉴权过滤器实现

package com.example.dubbo.filter;

import org.apache.dubbo.common.constants.CommonConstants;
import org.apache.dubbo.common.extension.Activate;
import org.apache.dubbo.rpc.*;
import org.springframework.stereotype.Component;

/**
 * 基于Dubbo SPI的自定义鉴权过滤器
 */
@Activate(group = CommonConstants.PROVIDER, order = 100) // 仅在服务端生效
@Component
public class AuthFilter implements Filter {

    @Override
    public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
        // 1. 从上下文获取调用信息
        RpcContext context = RpcContext.getContext();
        String token = context.getAttachment("auth_token");
        String appName = context.getAttachment("app_name");
        String clientIp = context.getRemoteHost();
        String methodName = invocation.getMethodName();
        
        // 2. 打印调用信息
        System.out.println("收到调用请求 - 方法: " + methodName + ", 客户端IP: " + clientIp + ", 应用名: " + appName);
        
        // 3. 执行鉴权逻辑
        if (!validateToken(token, appName)) {
            throw new RpcException("鉴权失败: 无效的token或应用名");
        }
        
        // 4. 检查方法级权限
        if (!hasMethodPermission(appName, methodName)) {
            throw new RpcException("权限不足: 应用" + appName + "没有调用" + methodName + "方法的权限");
        }
        
        // 5. 鉴权通过,继续执行调用
        return invoker.invoke(invocation);
    }
    
    /**
     * 验证token有效性
     */
    private boolean validateToken(String token, String appName) {
        // 实际项目中,这里应该从Nacos配置中心或数据库获取有效token列表
        // 简单示例:验证token格式和应用名
        if (token == null || appName == null) {
            return false;
        }
        
        // 从Nacos获取配置的有效应用和token映射关系
        // 这里简化处理,实际应通过NacosConfigService获取
        return "valid_token_123".equals(token) && "authorized_app".equals(appName);
    }
    
    /**
     * 检查是否有方法调用权限
     */
    private boolean hasMethodPermission(String appName, String methodName) {
        // 实际项目中,这里应该从Nacos配置中心获取应用与方法的权限映射
        // 简单示例:只有特定应用可以调用敏感方法
        if ("deleteUser".equals(methodName) && !"admin_app".equals(appName)) {
            return false;
        }
        return true;
    }
}

2.创建 SPI 配置文件,让 Dubbo 能够发现我们的过滤器:

# 注册自定义鉴权过滤器
authFilter=com.example.dubbo.filter.AuthFilter

3.服务提供者配置

application.yml

spring:
  application:
    name: dubbo-auth-provider

dubbo:
  application:
    name: dubbo-auth-provider
  registry:
    address: nacos://127.0.0.1:8848  # Nacos注册中心地址
    username: nacos                  # Nacos用户名
    password: nacos                  # Nacos密码
  protocol:
    name: dubbo
    port: 20880
  provider:
    filter: authFilter               # 启用自定义鉴权过滤器
    timeout: 3000

# Nacos配置中心
nacos:
  config:
    server-addr: 127.0.0.1:8848
    namespace: public

4.服务接口和实现

package com.example.dubbo.service;

public interface UserService {
    String getUserInfo(Long id);
    String deleteUser(Long id);
}
package com.example.dubbo.service.impl;

import com.example.dubbo.service.UserService;
import org.apache.dubbo.config.annotation.DubboService;

@DubboService(interfaceClass = UserService.class)
public class UserServiceImpl implements UserService {

    @Override
    public String getUserInfo(Long id) {
        return "User info for id: " + id + ", name: testUser";
    }

    @Override
    public String deleteUser(Long id) {
        return "User " + id + " deleted successfully";
    }
}

服务消费者配置和代码

package com.example.dubbo.consumer;

import com.example.dubbo.service.UserService;
import org.apache.dubbo.config.annotation.DubboReference;
import org.apache.dubbo.rpc.RpcContext;
import org.springframework.boot.ApplicationRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;

@SpringBootApplication
public class ConsumerApplication {

    @DubboReference
    private UserService userService;

    public static void main(String[] args) {
        SpringApplication.run(ConsumerApplication.class, args);
    }

    @Bean
    public ApplicationRunner runner() {
        return args -> {
            // 调用前设置鉴权信息
            RpcContext.getContext()
                    .setAttachment("auth_token", "valid_token_123")
                    .setAttachment("app_name", "authorized_app");
            
            // 调用普通方法
            String userInfo = userService.getUserInfo(1L);
            System.out.println("调用结果: " + userInfo);
            
            // 尝试调用需要管理员权限的方法
            try {
                String deleteResult = userService.deleteUser(1L);
                System.out.println("删除结果: " + deleteResult);
            } catch (Exception e) {
                System.out.println("调用deleteUser方法失败: " + e.getMessage());
            }
        };
    }
}

从 Nacos 配置中心获取鉴权规则

package com.example.dubbo.config;

import com.alibaba.nacos.api.NacosFactory;
import com.alibaba.nacos.api.config.ConfigService;
import com.alibaba.nacos.api.config.listener.Listener;
import com.alibaba.nacos.api.exception.NacosException;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.Properties;
import java.util.concurrent.Executor;

@Configuration
public class NacosConfig {

    @Value("${nacos.config.server-addr}")
    private String serverAddr;

    @Value("${nacos.config.namespace}")
    private String namespace;

    @Bean
    public ConfigService configService() throws NacosException {
        Properties properties = new Properties();
        properties.put("serverAddr", serverAddr);
        properties.put("namespace", namespace);
        
        ConfigService configService = NacosFactory.createConfigService(properties);
        
        // 监听鉴权规则配置变化
        String dataId = "dubbo-auth-rules";
        String group = "DEFAULT_GROUP";
        configService.addListener(dataId, group, new Listener() {
            @Override
            public void receiveConfigInfo(String configInfo) {
                System.out.println("收到新的鉴权规则配置: " + configInfo);
                // 更新本地缓存的鉴权规则
                AuthRuleCache.updateRules(configInfo);
            }
            
            @Override
            public Executor getExecutor() {
                return null;
            }
        });
        
        // 初始化加载配置
        String config = configService.getConfig(dataId, group, 5000);
        if (config != null) {
            AuthRuleCache.updateRules(config);
        }
        
        return configService;
    }
}

// 鉴权规则缓存类
class AuthRuleCache {
    private static String authRules;
    
    public static void updateRules(String rules) {
        authRules = rules;
        // 这里可以解析规则并存储到内存中
    }
    
    public static String getAuthRules() {
        return authRules;
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值