一、概念
责任链模式的定义:使多个对象都有机会处理请求,从而避免请求的发送者和接受者之间的耦合关系,将这个对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理他为止。
特点:
- 链上的每个对象都有机会处理请求
- 链上的每个对象都持有下一个要处理对象的引用
- 链上的某个对象无法处理当前请求,那么它会把相同的请求传给下一个对象
责任链模式满足了请求发送者与请求处理者之间的松耦合,抽象非核心的部分,以链式调用的方式对请求对象进行处理。
二、应用场景
- 多条件流程判断 权限控制
- ERP系统 流程审批 总经理、人事经理、项目经理
- Java过滤器的底层实现Filter
三、网关权限控制责任链模式
1.抽象处理者(Handler)角色:定义出一个处理请求的接口
2.具体处理者(ConcreteHandler)角色:具体处理者接到请求后,可以选择将请求处理掉,或者将请求传给下家。
在网关作为微服务程序的入口,拦截客户端所有的请求实现权限控制 ,比如先判断Api接口限流、黑名单、用户会话、参数过滤。
Api接口限流→黑名单拦截→用户会话→参数过滤
1 GatewayHandler抽象角色
public abstract class GatewayHandler {
protected GatewayHandler nextGatewayHandler;
/**
* 处理业务逻辑 return true 表示继续执行 false表示不继续执行..
*/
public abstract boolean service(Map<String,Object> param);
public void setNextGatewayHandler(GatewayHandler gatewayHandler) {
this.nextGatewayHandler = gatewayHandler;
}
//抽出一个模板:用于外部调用的入口
public void execute(Map<String,Object> param) {
boolean service = service(param);
if (nextGatewayHandler != null && service) {
nextGatewayHandler.execute(param);
}
}
}
2 具体Handler实现
@Component
public class CurrentLimitHandler extends GatewayHandler{
@Override
public boolean service(Map<String,Object> param) {
System.out.println("第一关网关限流判断....参数="+param);
param.put("code1","第一关网关限流判断");
return true;
}
}
@Component
public class BlacklistHandler extends GatewayHandler{
@Override
public boolean service(Map<String,Object> param) {
System.out.println("第二关黑名单拦截判断....参数="+param);
param.put("code2","第二关黑名单拦截判断");
return true;
}
}
@Component
public class ConversationHandler extends GatewayHandler {
@Override
public boolean service(Map<String,Object> param) {
System.out.println("第三关用户会话拦截判断....参数="+param);
param.put("code3","第三关用户会话拦截判断....");
return true;
}
}
3 FactoryHandler
public class FactoryHandler {
public static GatewayHandler getGatewayHandler() {
Map<String,Object> param=new HashMap<>();
param.put("businessCode","请求参数");
//初始化对象
GatewayHandler gatewayHandler1 = new CurrentLimitHandler();
GatewayHandler gatewayHandler2 = new BlacklistHandler();
GatewayHandler gatewayHandler3 = new ConversationHandler();
//设置引用,执行顺序设置
gatewayHandler1.setNextGatewayHandler(gatewayHandler2);
gatewayHandler2.setNextGatewayHandler(gatewayHandler3);
return gatewayHandler1;
}
public static void main(String[] args) {
GatewayHandler gatewayHandler = FactoryHandler.getGatewayHandler();
Map<String,Object> param = new HashMap<>();
gatewayHandler.execute(param);
System.out.println(param);
}
}
4 基于数据库实现
相关SQL语句
CREATE TABLE `gateway_handler` (
`ID` int NOT NULL AUTO_INCREMENT COMMENT '主键ID',
`handler_name` varchar(32) DEFAULT NULL COMMENT 'handler名称',
`handler_id` varchar(32) DEFAULT NULL COMMENT 'handler主键id',
`prev_handler_id` varchar(32) DEFAULT NULL,
`next_handler_id` varchar(32) DEFAULT NULL COMMENT '下一个handler',
PRIMARY KEY (`ID`)
) ENGINE=InnoDB AUTO_INCREMENT=19 DEFAULT CHARSET=utf8 COMMENT='权限表';
INSERT INTO `gateway_handler` VALUES (16, 'Api接口限流', 'currentLimitHandler', NULL, 'blacklistHandler');
INSERT INTO `gateway_handler` VALUES (17, '黑名单拦截', 'blacklistHandler', 'currentLimitHandler', 'conversationHandler');
INSERT INTO `gateway_handler` VALUES (18, '会话验证', 'conversationHandler', 'blacklistHandler', NULL);
5 GatewayHandlerService
@Service
@Slf4j
public class GatewayHandlerService {
@Autowired
private GatewayHandlerMapper gatewayHandlerMapper;
public GatewayHandler getGatewayHandler(Map<String,Object> param) {
//获取头节点
GatewayHandlerEntity firstGatewayHandler = gatewayHandlerMapper.getFirstGatewayHandler();
if (Objects.isNull(firstGatewayHandler)) {
log.info("未配置头节点,请注意");
return null;
}
String handlerId = firstGatewayHandler.getHandlerId();
if (StringUtils.isEmpty(handlerId)) {
log.info("未配置头节点,请注意");
return null;
}
//从容器中获取bean对象
GatewayHandler bean = SpringBootBeanUtil.getBean(handlerId, GatewayHandler.class);
if (Objects.isNull(bean)) {
log.info("容器中未找到");
return null;
}
//下一节点名称
String nextHandlerId = firstGatewayHandler.getNextHandlerId();
//记录当前对象
GatewayHandler currentBean = bean;
while (!StringUtils.isEmpty(nextHandlerId)) {
//从容器获取下一个执行handler
GatewayHandler nextBean = SpringBootBeanUtil.getBean(nextHandlerId, GatewayHandler.class);
if (Objects.isNull(nextBean)) {
break;
}
//查询db获取下一个handlerid
GatewayHandlerEntity nextGatewayHandler = gatewayHandlerMapper.getNextGatewayHandler(nextHandlerId);
if (Objects.isNull(nextGatewayHandler)) {
break;
}
nextHandlerId = nextGatewayHandler.getNextHandlerId();
currentBean.setNextGatewayHandler(nextBean);
currentBean = nextBean;
}
return bean;
}
}
6 SpringBootBeanUtil
获取Spring容器中的bean工具类
@Component
public class SpringBootBeanUtil implements ApplicationContextAware {
private static ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
if (SpringBootBeanUtil.applicationContext == null) {
SpringBootBeanUtil.applicationContext = applicationContext;
}
}
public static ApplicationContext getApplicationContext() {
return applicationContext;
}
/**
* 通过class获取Bean.
*/
public static <T> T getBean(Class<T> clazz) {
return getApplicationContext().getBean(clazz);
}
/**
* 通过name获取 Bean.
*/
public static Object getBean(String name) {
return getApplicationContext().getBean(name);
}
/**
* 通过name,以及Clazz返回指定的Bean
*/
public static <T> T getBean(String name, Class<T> clazz) {
return getApplicationContext().getBean(name, clazz);
}
}
7 GatewayHandlerMapper
@Mapper
public interface GatewayHandlerMapper {
/**
* 获取第一个GatewayHandler
*/
@Select("SELECT handler_name AS handlerName,handler_id AS handlerid ,prev_handler_id AS prevhandlerid ,next_handler_id AS nexthandlerid FROM gateway_handler WHERE prev_handler_id is null;;")
GatewayHandlerEntity getFirstGatewayHandler();
@Select("SELECT handler_name AS handlerName,handler_id AS handlerid ,prev_handler_id AS prevhandlerid ,next_handler_id AS nexthandlerid FROM gateway_handler WHERE handler_id=#{handlerId}")
GatewayHandlerEntity getNextGatewayHandler(String handlerId);
}
8 测试
@RestController
public class GatewayController {
@Autowired
private GatewayHandlerService gatewayHandlerService;
@RequestMapping("/client")
public String client() {
Map<String,Object> param = new HashMap<>();
GatewayHandler firstGatewayHandler = gatewayHandlerService.getGatewayHandler(param);
firstGatewayHandler.execute(param);
return param.toString();
}
}