天天看點

Java微服務設計模式實戰:網關權限控制_責任鍊模式

作者:架構淺水灣

Java微服務設計模式實戰:網關權限控制_責任鍊模式

責任鍊模式

Java微服務設計模式實戰:網關權限控制_責任鍊模式

什麼是責任鍊模式

用戶端發出一個請求,鍊上的對象都有機會來處理這一請求,而用戶端不需要知道誰是具體的處理對象。這樣就實作了請求者和接受者之間的解耦,并且在用戶端可以實作動态的組合職責鍊。使程式設計更有靈活性。

定義:使多個對象都有機會處理請求,進而避免了請求的發送者和接受者之間的耦合關系。将這些對象連成一條鍊,并沿着這條鍊傳遞該請求,直到有對象處理它為止。其過程實際上是一個遞歸調用。

要點主要是:

 1、有多個對象共同對一個任務進行處理。

2、這些對象使用鍊式存儲結構,形成一個鍊,每個對象知道自己的下一個對象。

3、一個對象對任務進行處理,可以添加一些操作後将對象傳遞個下一個任務。也可以在此對象上結束任務的處理,并結束任務。

4、用戶端負責組裝鍊式結構,但是用戶端不需要關心最終是誰來處理了任務。

責任鍊模式類結構圖

 1.抽象處理者(Handler)角色:定義出一個處理請求的接口。如果需要,接口可以定義 出一個方法以設定和傳回對下家的引用。這個角色通常由一個Java抽象類或者Java接口實作。上圖中Handler類的聚合關系給出了具體子類對下家的引用,抽象方法handleRequest()規範了子類處理請求的操作。

 2.具體處理者(ConcreteHandler)角色:具體處理者接到請求後,可以選擇将請求處理掉,或者将請求傳給下家。由于具體處理者持有對下家的引用,是以,如果需要,具體處理者可以通路下家

責任鍊模式優缺點

優點:

職責鍊模式的最主要功能就是:動态組合,請求者和接受者解耦。

請求者和接受者松散耦合:請求者不需要知道接受者,也不需要知道如何處理。每個職責對象隻負責自己的職責範圍,其他的交給後繼者。各個元件間完全解耦。

動态組合職責:職責鍊模式會把功能分散到單獨的職責對象中,然後在使用時動态的組合形成鍊,進而可以靈活的配置設定職責對象,也可以靈活的添加改變對象職責。

缺點:

産生很多細粒度的對象:因為功能處理都分散到了單獨的職責對象中,每個對象功能單一,要把整個流程處理完,需要很多的職責對象,會産生大量的細粒度職責對象。

不一定能處理:每個職責對象都隻負責自己的部分,這樣就可以出現某個請求,即使把整個鍊走完,都沒有職責對象處理它。這就需要提供預設處理,并且注意構造鍊的有效性。

責任鍊模式應用場景

多條件流程判斷 權限控制

ERP系統 流程審批 總經理、人事經理、項目經理

Java過濾器的底層實作Filter

比如:在Java過濾器中用戶端發送請求到伺服器端,過濾會經過參數過濾、session過濾、表單過濾、隐藏過濾、檢測請求頭過濾

網關權限控制責任鍊模式

在網關作為微服務程式的入口,攔截用戶端所有的請求實作權限控制 ,比如先判斷Api接口限流、黑名單、使用者會話、參數過濾。

Api接口限流→黑名單攔截→使用者會話→參數過濾

GatewayHandler抽象角色

public abstract class GatewayHandler {
protected GatewayHandler nextGatewayHandler;
/**
* 處理業務邏輯
*
* @return true 表示繼續執行 false表示不繼續執行..
*/
public abstract void service();
public void setHandler(GatewayHandler gatewayHandler) {
this.nextGatewayHandler = gatewayHandler;
}
protected void nextService(){
if(nextGatewayHandler!=null){
nextGatewayHandler.service();;
}
}
}



(具體Handler實作)
@Component
public class CurrentLimitHandler extends GatewayHandler {
@Override
public void service() {
System.out.println("第一關網關限流判斷....");
nextService();
}
}



@Component
public class ConversationHandler extends GatewayHandler {
@Override
public void service() {
System.out.println("第三關使用者會話攔截判斷....");
nextService();
}
}



@Component
public class BlacklistHandler extends GatewayHandler {
@Override
public void service() {
System.out.println("第二關黑名單攔截判斷....");
nextService();
}
}
@Component
public class ConversationHandler extends GatewayHandler {
@Override
public void service() {
System.out.println("第三關使用者會話攔截判斷....");
nextService();
}
}





FactoryHandler
public class FactoryHandler {
public static GatewayHandler getGatewayHandler() {
GatewayHandler gatewayHandler1 = new CurrentLimitHandler();
GatewayHandler gatewayHandler2 = new BlacklistHandler();
gatewayHandler1.setHandler(gatewayHandler2);
GatewayHandler gatewayHandler3 = new ConversationHandler();
gatewayHandler2.setHandler(gatewayHandler3);
return gatewayHandler1;
}
}


基于資料庫實作
相關SQL語句
CREATE TABLE `gateway_handler` (
`ID` int(11) 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='權限表';

-- ----------------------------
-- Records of gateway_handler
-- ----------------------------
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);
GatewayHandlerService
@Component
public class GatewayHandlerService {
@Autowired
private GatewayHandlerMapper gatewayHandlerMapper;
private GatewayHandler firstGatewayHandler;
public GatewayHandler getDbGatewayHandler() {
if (firstGatewayHandler != null) {
return firstGatewayHandler;
}
// 1.擷取第一個GatewayHandler資訊
GatewayHandlerEntity firstGatewayHandlerEntity = gatewayHandlerMapper.getFirstGatewayHandler();
if (firstGatewayHandlerEntity == null) {
return null;
}
// 2.擷取第一個firstGatewayHandler spring容器中的id
String handlerBeanId = firstGatewayHandlerEntity.getHandlerId();
// 3.從spring容器中擷取對應的對象 firstGatewayHandler
GatewayHandler firstGatewayHandler = SpringUtils.getBean(handlerBeanId, GatewayHandler.class);
// 4.使用white循環 設定下一個節點 同時定義循環周遊臨時對象
GatewayHandler tempGatewayHandler = firstGatewayHandler;
// 5.擷取下一個節點
String nextHandlerBeanId = firstGatewayHandlerEntity.getNextHandlerId();
while (!StringUtils.isEmpty(nextHandlerBeanId)) {
GatewayHandlerEntity nextGatewayHandlerEntity = gatewayHandlerMapper.getByHandler(nextHandlerBeanId);
if (nextGatewayHandlerEntity == null) {
break;
}
// 6.從springboot容器擷取下一個handler 對象
String tempNextHandlerBeanId = nextGatewayHandlerEntity.getHandlerId();
GatewayHandler nextGatewayHandler = SpringUtils.getBean(tempNextHandlerBeanId, GatewayHandler.class);
// 7.設定目前handler下一個handler對象
tempGatewayHandler.setHandler(nextGatewayHandler);
tempGatewayHandler = nextGatewayHandler;
// 8.循環周遊下一個節點
nextHandlerBeanId = nextGatewayHandlerEntity.getNextHandlerId();
}
this.firstGatewayHandler = firstGatewayHandler;
return firstGatewayHandler;
}
}
           

資料庫通路層

GatewayHandlerMapper
public interface GatewayHandlerMapper {
/**
* 擷取第一個GatewayHandler
*
* @return
*/
@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;;")
public 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}")
public GatewayHandlerEntity getByHandler(String handlerId);
}
@Data
public class GatewayHandlerEntity implements Serializable, Cloneable {
/** 主鍵ID */
private Integer id;
/** handler名稱 */
private String handlerName;
/** handler主鍵id */
private String handlerId;
/** 下一個handler */
private String nextHandlerId;
}           

繼續閱讀