实现说明
整体流程:
- 服务提供者启动时会注册到 Nacos,并启用自定义的
AuthFilter- 客户端调用服务前,通过
RpcContext设置鉴权信息(token 和应用名)- 服务端的
AuthFilter会拦截请求,验证鉴权信息- 验证通过则继续调用,否则抛出异常
关键实现点:
- 使用
@Activate注解指定过滤器在服务端生效- 通过 SPI 机制注册过滤器
- 在
invoke方法中实现鉴权逻辑- 利用
RpcContext传递和获取调用上下文信息动态配置:
- 进阶实现中添加了 Nacos 配置中心客户端
- 可以动态获取和更新鉴权规则,无需重启服务
使用时,需要先启动 Nacos 服务器,然后分别启动服务提供者和消费者,即可看到鉴权效果。你可以根据实际业务需求修改
validateToken和hasMethodPermission方法中的鉴权逻辑。
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;
}
}
1004

被折叠的 条评论
为什么被折叠?



