大家好,我是飄渺!
在這個系列文章中曾經介紹過在SpringCloud體系下如何防止前端請求繞過網關直接到達后端微服務,今天我們要反其道而行之,介紹在SpringCloud體系中如何防止內部隱私介面被網關呼叫,
看到這里可能有的同學會有點暈,怎么還有這種業務場景呢,別急,咱們先回顧一下我們的業務場景,
友情提示,這是系列文章,歡迎持續關注!此文在流量變少時會轉入收費專欄,且看且珍惜!
業務場景

客戶端通過網關呼叫OrderService服務獲取資料,OrderService通過Feign呼叫AccountService服務,而當AccountService提供對應的Feign介面后,客戶端是可以通過網關直接呼叫AccountService介面的,
現在假設AccountService提供的介面包含了部分隱私資料,只允許內部呼叫協助OrderService進行業務邏輯處理,不允許客戶端直接獲取,此時咱們需要怎么做?
業務實戰
我們先通過代碼將原始的流程實作出來,即通過網關呼叫OrderService的OrderController,然后在OrderController中通過Feign呼叫AccountService的AccountController,為了便于閱讀,文章中洗掉了部分無用代碼,
模擬實作
- 入口 OrderController
public class OrderController {
private final OrderService orderService;
private final AccountClient accountClient;
@GetMapping("/order/{orderNo}")
public ResultData<OrderDTO> getById(@PathVariable("orderNo") String orderNo){
OrderDTO orderDTO = orderService.selectByNo(orderNo);
ResultData<String> secretValue = accountClient.getSecretValue();
log.info(secretValue);
return ResultData.success(orderDTO);
}
}
在OrderController中通過AccountClient呼叫AccountService
ResultData<String> secretValue = accountClient.getSecretValue();
- Feign介面
public interface AccountApi {
...
@GetMapping("/account/getSecretValue")
ResultData<String> getSecretValue();
...
}
- AccountController實作
@RestController
@Log4j2
@Api(tags = "account介面")
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class AccountController implements AccountApi {
/**
* 隱私介面,禁止通過網關訪問
*/
@Override
@GetMapping("/account/getSecretValue")
public ResultData<String> getSecretValue() {
return ResultData.success("隱私介面,禁止通過網關訪問");
}
}
正如我們前面所說,一旦提供了Feign介面,在默認情況下我們可以直接通過網關訪問getSecretValue()方法,那怎么確保這個方法不讓外部呼叫呢?
解決方案
網上現在大部分的解決辦法是基于黑名單機制,即將這些介面放入“黑名單”中存盤起來,在網關啟動時讀取黑名單配置,然后校驗是否在黑名單中,
這種辦法確實也可以,但是總感覺不夠靈活,而且實作也比較繁瑣,這里就不展開了,
我們今天介紹的是利用訪問路徑來實作,非常簡單輕便,
實作原理
我們需要借助介面路徑規范來實作,即給介面指定訪問路徑時采用這樣的格式 : /訪問控制/介面
訪問控制可以有以下幾個規則(參考JAVA包規范),可根據業務需要進行擴展,
pb - public 所有請求均可訪問
pt - protected 需要進行token認證通過后方可訪問
pv - private 無法通過網關訪問,只能微服務內部呼叫
df - default 網關請求token認證,并且請求引數和回傳結果進行加解密
...
有了這套介面規范以后,我們就可以靈活控制介面訪問權限,然后在網關對介面路徑進行校驗,如果命中對應的訪問控制規則就進行對應的邏輯處理,
代碼實戰
既然知道了實作原理,那寫代碼就很簡單了,

- 修改介面訪問路徑,遵循介面路徑規范
public interface AccountApi {
@GetMapping("/pv/account/getSecretValue")
ResultData<String> getSecretValue();
}
修改feign的訪問路徑,
@RestController
@Log4j2
@Api(tags = "account介面")
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class AccountController implements AccountApi {
/**
* 隱私介面,禁止通過網關訪問
*/
@Override
@GetMapping("/pv/account/getSecretValue")
public ResultData<String> getSecretValue() {
return ResultData.success("隱私介面,禁止通過網關訪問");
}
}
修改介面實作類的訪問路徑,這里需要與Feign的路徑保持一致,
- 網關自定義攔截器進行介面校驗
@Component
@Order(0)
@Slf4j
public class GatewayRequestFilter implements GlobalFilter {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
//獲取請求路徑
String rawPath = exchange.getRequest().getURI().getRawPath();
if(isPv(rawPath)){
throw new HttpServerErrorException(HttpStatus.FORBIDDEN,"can't access private API");
}
return chain.filter(newExchange);
}
/**
* 判斷是否內部私有方法
* @param requestURI 請求路徑
* @return boolean
*/
private boolean isPv(String requestURI) {
return isAccess(requestURI,"/pv");
}
/**
* 網關訪問控制校驗
*/
private boolean isAccess(String requestURI, String access) {
//后端標準請求路徑為 /訪問控制/請求路徑
int index = requestURI.indexOf(access);
return index >= 0 && StringUtils.countOccurrencesOf(requestURI.substring(0,index),"/") < 1;
}
}
通過上面簡單兩步我們就能實作本文提出的問題了,接下來我們測驗一下,
測驗
- 直接訪問后端服務,提示無法訪問

- 通過OrderService訪問后端服務正常訪問

小結
讓內部隱私介面不被外部訪問,我相信做微服務開發的同學基本都會遇到,本文中提供的解決方案代碼量很少而且介面路徑規范可以根據自己的業務規則進行修改擴展,推薦大家使用,其實代碼不是關鍵,關鍵在于要讓團隊共同遵守這個介面規范,思想比實作更重要,
我是飄渺Jam,一名寫代碼的架構師,做架構的程式員,期待您的轉發與關注,咱們下期見!
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/291915.html
標籤:其他
下一篇:11-單點登錄系統微服務版實踐
