【Nacos-安全與限流機制健全06 】

文章目錄

  • Nacos安全機制介紹
  • Nacos代碼實現
  • Nacos限流機制
  • Nacos限流的代碼實現

Nacos安全機制介紹

一、Nacos安全控制機制
Nacos 提供了多種安全控制機制,以保證服務和配置的訪問安全:

  1. 身份驗證 (Authentication)
    Nacos 支持用戶身份驗證來防止未授權的訪問。其實現方式如下:
    Basic Authentication:通過 HTTP 請求頭傳遞用戶的認證信息(如 Authorization),Nacos 會校驗用戶名和密碼。
    OAuth2 / Token 認證:除了基本的用戶名密碼認證外,Nacos 也可以配置 OAuth2 認證機制,允許使用第三方認證系統(例如 LDAP 或自定義身份驗證服務)。

  2. 權限控制 (Authorization)
    在身份驗證的基礎上,Nacos 提供了基于角色的權限控制:
    資源授權:通過注解的方式對 API 接口進行權限控制,例如使用 @Secured 注解標記需要權限驗證的接口。每個接口可以指定具體的權限(如讀、寫、刪除等)。
    角色分配:用戶可以被分配不同的角色,角色對應不同的權限,例如,管理員擁有所有權限,普通用戶僅能訪問部分資源。

Nacos代碼實現

權限的核心注解類

@Retention(RetentionPolicy.RUNTIME)
public @interface Secured {/***  READ,WRITE,DELETE,UPDATE;默認讀權限*/ActionTypes action() default ActionTypes.READ;/**定義與請求相關的資源名稱。資源名稱用于標識請求操作的是哪一類資源(例如:配置、服務等)。默認為空字符串,但你可以指定具體的資源名稱。*/String resource() default StringUtils.EMPTY;/**表示請求使用的簽名類型。默認為 SignType.NAMING。SignType 可能是一個枚舉,表示不同的簽名機制,例如 NAMING、HASH、API_KEY 等。*/String signType() default SignType.NAMING;/**允許使用自定義的資源解析器類來解析資源。默認為 DefaultResourceParser.class,但你可以指定自定義的類來實現 ResourceParser,定制資源的解析方式。*/Class<? extends ResourceParser> parser() default DefaultResourceParser.class;/**允許為資源指定額外的標簽,這些標簽會作為鍵值對注入到 Resource 對象中,用于細化資源的授權控制。標簽是以字符串形式傳遞的,可以用于更精細的權限控制。*/String[] tags() default {};

servlet過濾器,在我們的請求進來時,我們判斷當前的請求是否還有@Secured

public class AuthFilter implements Filter {private final AuthConfigs authConfigs;private final ControllerMethodsCache methodsCache;private final HttpProtocolAuthService protocolAuthService;public AuthFilter(AuthConfigs authConfigs, ControllerMethodsCache methodsCache) {this.authConfigs = authConfigs;this.methodsCache = methodsCache;this.protocolAuthService = new HttpProtocolAuthService(authConfigs);this.protocolAuthService.initialize();}@Overridepublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)throws IOException, ServletException {//未啟用權限驗證if (!authConfigs.isAuthEnabled()) {chain.doFilter(request, response);return;}HttpServletRequest req = (HttpServletRequest) request;HttpServletResponse resp = (HttpServletResponse) response;//. 支持 user-agent 白名單跳過鑒權if (authConfigs.isEnableUserAgentAuthWhite()) {String userAgent = WebUtils.getUserAgent(req);if (StringUtils.startsWith(userAgent, Constants.NACOS_SERVER_HEADER)) {chain.doFilter(request, response);return;}//服務身份頭鑒權(用于集群內部通信)} else if (StringUtils.isNotBlank(authConfigs.getServerIdentityKey()) && StringUtils.isNotBlank(authConfigs.getServerIdentityValue())) {String serverIdentity = req.getHeader(authConfigs.getServerIdentityKey());if (StringUtils.isNotBlank(serverIdentity)) {if (authConfigs.getServerIdentityValue().equals(serverIdentity)) {chain.doFilter(request, response);return;}Loggers.AUTH.warn("Invalid server identity value for {} from {}", authConfigs.getServerIdentityKey(),req.getRemoteHost());}} else {resp.sendError(HttpServletResponse.SC_FORBIDDEN,"Invalid server identity key or value, Please make sure set `nacos.core.auth.server.identity.key`"+ " and `nacos.core.auth.server.identity.value`, or open `nacos.core.auth.enable.userAgentAuthWhite`");return;}try {//獲取請求的方法Method method = methodsCache.getMethod(req);//方法為空,直接結束,繼續進行下面的校驗if (method == null) {chain.doFilter(request, response);return;}// 判斷當前注解是否開啟if (method.isAnnotationPresent(Secured.class) && authConfigs.isAuthEnabled()) {if (Loggers.AUTH.isDebugEnabled()) {Loggers.AUTH.debug("auth start, request: {} {}", req.getMethod(), req.getRequestURI());}Secured secured = method.getAnnotation(Secured.class);if (!protocolAuthService.enableAuth(secured)) {chain.doFilter(request, response);return;}Resource resource = protocolAuthService.parseResource(req, secured);//解析資源IdentityContext identityContext = protocolAuthService.parseIdentity(req);//解析身份boolean result = protocolAuthService.validateIdentity(identityContext, resource);//校驗身份和權限//校驗失敗拋出異常if (!result) {// TODO Get reason of failurethrow new AccessException("Validate Identity failed.");}injectIdentityId(req, identityContext);String action = secured.action().toString();result = protocolAuthService.validateAuthority(identityContext, new Permission(resource, action));if (!result) {// TODO Get reason of failurethrow new AccessException("Validate Authority failed.");}}chain.doFilter(request, response);} catch (AccessException e) {if (Loggers.AUTH.isDebugEnabled()) {Loggers.AUTH.debug("access denied, request: {} {}, reason: {}", req.getMethod(), req.getRequestURI(),e.getErrMsg());}resp.sendError(HttpServletResponse.SC_FORBIDDEN, e.getErrMsg());} catch (IllegalArgumentException e) {resp.sendError(HttpServletResponse.SC_BAD_REQUEST, ExceptionUtil.getAllExceptionMsg(e));} catch (Exception e) {Loggers.AUTH.warn("[AUTH-FILTER] Server failed: ", e);resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Server failed, " + e.getMessage());}}

注冊我們的過濾器

@Configuration
public class AuthConfig {@Beanpublic FilterRegistrationBean<AuthFilter> authFilterRegistration(AuthFilter authFilter) {FilterRegistrationBean<AuthFilter> registration = new FilterRegistrationBean<>();registration.setFilter(authFilter);//注冊權限過濾器registration.addUrlPatterns("/*");//任何的接口都允許registration.setName("authFilter");//設置名稱registration.setOrder(6);//設置優先級return registration;}@Beanpublic AuthFilter authFilter(AuthConfigs authConfigs, ControllerMethodsCache methodsCache) {return new AuthFilter(authConfigs, methodsCache);}
}

Nacos限流機制

Nacos 在流量控制和限流方面,提供了以下機制來保護系統免受超載:

  1. 請求限流 (TpsControl)
    TpsControl 注解:通過 @TpsControl 注解,可以對某個方法進行限流,控制每秒鐘請求的數量(TPS)。這對于防止 Nacos 服務受到過多請求而導致性能下降非常重要。
@TpsControl(pointName = "nacosConfigRequest", limit = 100)
public Response handleConfigRequest(Request request) {// 限流請求,限制每秒最多100個請求
}

TpsControlManager:負責管理所有限流點的狀態和配置,可以動態修改每個點的 TPS 限制。
通過使用 TpsControl,可以保證 Nacos 服務在流量高峰期間不會因為流量過大而導致崩潰或服務不可用。
2. 請求重試與超時控制
請求重試機制:Nacos 內部有一套請求重試機制,允許在網絡出現故障時進行重試。請求會根據一定的規則進行重試,避免出現單點故障導致系統不可用。
請求超時控制:對于某些請求,Nacos 會設置超時時間,防止長時間沒有響應的請求占用系統資源。
3. 長輪詢控制
對于長輪詢請求,Nacos 會控制每個客戶端的最大并發請求數量,防止單個客戶端占用過多資源而導致其他客戶端的請求被延遲。
4. 動態限流(基于請求屬性)
通過 請求屬性(如請求路徑、請求參數、客戶端 IP 等)可以動態地進行流量限制。例如,可以針對某些 API 或某些高風險的操作(如配置更新、服務注冊等)設置更嚴格的限流策略。

Nacos限流的代碼實現

限流的注解實現

@Retention(RetentionPolicy.RUNTIME)
public @interface TpsControl {/*** 控制點的別名(可選)。* 主要是為了給控制點提供更人性化的名稱,也可能用于日志或監控中展示。*/String name() default "";/*** 必填。真正用于限流控制的“控制點名稱”。* 這是唯一標識某個接口或操作被限流管理的依據。*/String pointName();}

利用SpringMvc的攔截器攔哦判斷限流規則

public class NacosHttpTpsControlInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {try {if (handler instanceof HandlerMethod) {Method method = ((HandlerMethod) handler).getMethod();if (method.isAnnotationPresent(TpsControl.class) && TpsControlConfig.isTpsControlEnabled()) {TpsControl tpsControl = method.getAnnotation(TpsControl.class);String pointName = tpsControl.pointName();HttpTpsCheckRequestParser parser = HttpTpsCheckRequestParserRegistry.getParser(pointName);TpsCheckRequest httpTpsCheckRequest = null;if (parser != null) {httpTpsCheckRequest = parser.parse(request);}if (httpTpsCheckRequest == null) {httpTpsCheckRequest = new TpsCheckRequest();}httpTpsCheckRequest.setPointName(pointName);TpsCheckResponse checkResponse = ControlManagerCenter.getInstance().getTpsControlManager().check(httpTpsCheckRequest);if (!checkResponse.isSuccess()) {generate503Response(request, response, checkResponse.getMessage());return false;}}}} catch (Throwable throwable) {Loggers.TPS.error("Error to check tps control", throwable);}return true;}void generate503Response(HttpServletRequest request, HttpServletResponse response, String message) {try {// Disable cache.response.setHeader("Pragma", "no-cache");response.setDateHeader("Expires", 0);response.setHeader("Cache-Control", "no-cache,no-store");response.setStatus(HttpServletResponse.SC_SERVICE_UNAVAILABLE);response.getWriter().println(message);} catch (Exception ex) {Loggers.TPS.error("Error to generate tps 503 response", ex);}}
}

Tps限流的規則

public class TpsControlRequestFilter extends AbstractRequestFilter {TpsControlManager tpsControlManager = ControlManagerCenter.getInstance().getTpsControlManager();@Overrideprotected Response filter(Request request, RequestMeta meta, Class handlerClazz) {Method method = null;try {//獲取當錢的方法method = getHandleMethod(handlerClazz);} catch (NacosException e) {return null;}//如果當前的方法被注解TpsControl修飾,并且限流開關已經開啟if (method.isAnnotationPresent(TpsControl.class) && TpsControlConfig.isTpsControlEnabled()) {try {//獲取注解信息TpsControl tpsControl = method.getAnnotation(TpsControl.class);//獲取自定義的限流名稱String pointName = tpsControl.pointName();TpsCheckRequest tpsCheckRequest = null;//校驗解析的名稱String parseName = StringUtils.isBlank(tpsControl.name()) ? pointName : tpsControl.name();RemoteTpsCheckRequestParser parser = RemoteTpsCheckRequestParserRegistry.getParser(parseName);if (parser != null) {tpsCheckRequest = parser.parse(request, meta);}//判斷tps檢查請求是否為nullif (tpsCheckRequest == null) {tpsCheckRequest = new TpsCheckRequest();}tpsCheckRequest.setPointName(pointName);//核心點TpsCheckResponse check = tpsControlManager.check(tpsCheckRequest);//如果返回的結果為失敗if (!check.isSuccess()) {Response response;try {response = super.getDefaultResponseInstance(handlerClazz);response.setErrorInfo(NacosException.OVER_THRESHOLD,"Tps Flow restricted:" + check.getMessage());return response;} catch (Exception e) {com.alibaba.nacos.plugin.control.Loggers.TPS.warn("Tps check fail , request: {},exception:{}", request.getClass().getSimpleName(),e);return null;}}} catch (Throwable throwable) {com.alibaba.nacos.plugin.control.Loggers.TPS.warn("Tps check exception , request: {},exception:{}", request.getClass().getSimpleName(),throwable);}}return null;}
}

檢查當前的Tps

    public TpsCheckResponse check(TpsCheckRequest tpsRequest) {//判斷point中是否包含當前方法的名稱if (points.containsKey(tpsRequest.getPointName())) {try {//包含就直接回去,返回成功return points.get(tpsRequest.getPointName()).applyTps(tpsRequest);} catch (Throwable throwable) {Loggers.TPS.warn("[{}]apply tps error,error={}", tpsRequest.getPointName(), throwable);}}//不包含也直接返回成功的結果return new TpsCheckResponse(true, TpsResultCode.CHECK_SKIP, "skip");}public TpsCheckResponse applyTps(BarrierCheckRequest barrierCheckRequest) {//添加當前的請求的數量rateCounter.add(barrierCheckRequest.getTimestamp(), barrierCheckRequest.getCount());//返回正確的請求響應return new TpsCheckResponse(true, TpsResultCode.PASS_BY_POINT, "success");}//請求的滑動窗口算法public TpsSlot createSlotIfAbsent(long timeStamp) {//startTime 是滑動窗口的起始時間戳。//distance 是當前時間戳距離 startTime 的偏移。long distance = timeStamp - startTime;//如果時間戳早于 startTime(即 distance < 0),為了避免負數下標,先加上整個滑動窗口的周期總時長(例如 10秒 * 1000ms)。// 然后除以每個時間槽的周期(一般為 1000ms)以得到偏移槽位數。long diff = (distance < 0 ? distance + getPeriod().toMillis(1) * DEFAULT_RECORD_SIZE : distance) / getPeriod().toMillis(1);//當前時間戳應該屬于哪個時間窗口(對齊到周期的時間點)。long currentWindowTime = startTime + diff * getPeriod().toMillis(1);//用偏移量對槽位數量取模,確定當前時間窗口的槽位 index。int index = (int) diff % DEFAULT_RECORD_SIZE;TpsSlot tpsSlot = slotList.get(index);//如果槽的時間不是當前窗口時間(說明槽是舊的),重置它。if (tpsSlot.time != currentWindowTime) {tpsSlot.reset(currentWindowTime);}//返回這個屬于當前窗口的槽。return slotList.get(index);}//重置public void reset(long second) {synchronized (this) {if (this.time != second) {this.time = second;countHolder.count.set(0L);countHolder.interceptedCount.set(0);}}}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/77361.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/77361.shtml
英文地址,請注明出處:http://en.pswp.cn/web/77361.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

自建開源遠程協助服務RustDesk —— 筑夢之路

開源項目 # 服務端https://github.com/rustdesk/rustdesk-server.git# 客戶端https://github.com/rustdesk/rustdesk.git 搭建服務端 需要使用的端口、協議 hbbs - RustDesk ID 注冊服務器 hbbr - RustDesk 中繼服務器默認情況下&#xff0c;hbbs 監聽 21115(tcp) , 21…

Jmeter中同步定時器使用注意點

1.設置數量不可大于總線程數量&#xff0c;不然會一直等待 2.設置數量必須與總線程數量成整數倍數&#xff0c;不然還是要一直等。 3.當配置的數量小于線程數時&#xff0c;最好把循環打開&#xff0c;避免最后一次未準備好的線程數量達不到并發數。

作為高速通道光纖傳輸模式怎么理解以及到底有哪些?

光纖的傳輸模式主要取決于光纖的結構(如纖芯直徑和折射率分布),不同模式對應光波在光纖中傳播的不同路徑和電磁場分布。以下是光纖傳輸模式的主要分類及特點: 1. 單模光纖(Single-Mode Fiber, SMF) 核心特點: 纖芯直徑極小(通常為 8-10微米),僅允許光以單一模式(…

小程序Npm package entry file not found?

修改依賴包的入口文件 看是不是cjs&#xff0c;小程序不支持cjs

Android HAL HIDL

1 Android HAL HIDL 1.1 Android中查看有哪些HIDL HAL HIDL是Treble Interface的一部分。 adb root adb shell # lshal 1.2 Android打印C調用棧 #include <utils/CallStack.h> 在需要打印的地方加如下的定義。 android::CallStack stack("oem"); logcat | g…

【AI 加持下的 Python 編程實戰 2_11】DIY 拓展:從掃雷小游戲開發再探問題分解與 AI 代碼調試能力(下)

&#xff08;接 上篇&#xff09; 5 復盤與 Copilot 的交互過程 前面兩篇文章分別涵蓋了掃雷游戲的問題分解和代碼實現過程&#xff0c;不知道各位是否會有代碼一氣呵成的錯覺&#xff1f;實際上&#xff0c;為了達到最終效果&#xff08;如下所示&#xff09;&#xff0c;我…

游戲狀態管理:用Pygame實現場景切換與暫停功能

游戲狀態管理:用Pygame實現場景切換與暫停功能 在開發游戲時,管理游戲的不同狀態(如主菜單、游戲進行中、暫停等)是非常重要的。這不僅有助于提升玩家的游戲體驗,還能使代碼結構更加清晰。本文將通過一個簡單的示例,展示如何使用Pygame庫來實現游戲中的場景切換和暫停功…

Java后端開發day36--源碼解析:HashMap

&#xff08;以下內容均來自上述課程&#xff09; 1. HashMap&#xff08;一&#xff09; 底層&#xff1a;數組鏈表紅黑樹 1.1 前提準備 查看源碼&#xff1a;選中HashMap–ctrlB 小細節&#xff1a;快捷鍵ctrlf12–跳出目錄結構 藍色圓圈&#xff1a;class 證明是類名粉…

RT-Thread學習筆記(四)

RT-Thread學習筆記 線程間同步信號量信號量的使用和管理動態創建信號量靜態創建信號量獲取信號量信號量同步實列互斥量互斥量的使用和管理互斥量動態創建互斥量靜態創建互斥量獲取和釋放互斥量實例事件集事件集的使用和管理動態創建事件集靜態初始化事件集發送和接收事件事件集…

element ui el-col的高度不一致導致換行

問題&#xff1a;ell-col的高度不一致導致換行&#xff0c;刷新后審查el-col的高度一致 我這邊是el-col寫的span超過了24&#xff0c;自行換行&#xff0c;測試發現初次進入里面的高度渲染的不一致&#xff0c;有的是51px有的是51.5px 問題原因分析 Flex布局換行機制 Elemen…

現代化Android開發:Compose提示信息的最佳封裝方案

在 Android 開發中&#xff0c;良好的用戶反饋機制至關重要。Jetpack Compose 提供了現代化的 UI 構建方式&#xff0c;但提示信息(Toast/Snackbar)的管理往往顯得分散。本文將介紹如何優雅地封裝提示信息&#xff0c;提升代碼可維護性。 一、基礎封裝方案 1. 簡單 Snackbar …

【C++語法】類和對象(2)

4.類和對象&#xff08;2&#xff09; 文章目錄 4.類和對象&#xff08;2&#xff09;類的六個默認成員函數(1)構造函數&#xff1a;構造函數特點含有缺省參數的構造函數構造函數特點&#xff08;續&#xff09;注意事項構造函數補充 前面總結了有關對象概念&#xff0c;對比 C…

【自然語言處理與大模型】vLLM部署本地大模型②

舉例上一篇文章已經過去了幾個月&#xff0c;大模型領域風云變幻&#xff0c;之前的vLLM安裝稍有過時&#xff0c;這里補充一個快速安裝教程&#xff1a; # 第一步&#xff1a;創建虛擬環境并激活進入 conda create -n vllm-0.8.4 python3.10 -y conda activate vllm-0…

26 Arcgis軟件常用工具有哪些

一、畫圖改圖工具&#xff08;矢量編輯&#xff09;? ?挪位置工具&#xff08;移動工具&#xff09;? 干哈的&#xff1f;?選中要素?&#xff08;比如地塊、道路&#xff09;直接拖到新位置&#xff0c;或者用坐標?X/Y偏移?批量移動&#xff0c;適合“整體搬家”。 ?磁…

QNX/LINUX/Android系統動態配置動態庫.so文件日志打印級別的方法

背景 通常我們會在量產的產品上&#xff0c;配置軟件僅打印少量日志&#xff0c;以提升產品的運行性能。同時我們要考慮預留方法讓軟件能夠擁有能力可以在燒錄版本后能夠通過修改默寫配置&#xff0c;打印更多日志。因為量產后的軟件通常開啟熔斷與加密&#xff0c;不能夠輕松…

WebGL圖形編程實戰【4】:光影交織 × 逐片元光照與渲染技巧

現實世界中的物體被光線照射時&#xff0c;會反射一部分光。只有當反射光線進人你的眼睛時&#xff0c;你才能夠看到物體并辯認出它的顏色。 光源類型 平行光&#xff08;Directional Light&#xff09;&#xff1a;光線是相互平行的&#xff0c;平行光具有方向。平行光可以看…

【Hive入門】Hive基礎操作與SQL語法:DDL操作全面指南

目錄 1 Hive DDL操作概述 2 數據庫操作全流程 2.1 創建數據庫 2.2 查看數據庫 2.3 使用數據庫 2.4 修改數據庫 2.5 刪除數據庫 3 表操作全流程 3.1 創建表 3.2 查看表信息 3.3 修改表 3.4 刪除表 4 分區與分桶操作 4.1 分區操作流程 4.2 分桶操作 5 最佳實踐與…

YOLO數據處理

YOLO&#xff08;You Only Look Once&#xff09;的數據處理流程是為了解決目標檢測領域的核心挑戰&#xff0c;核心目標是為模型訓練和推理提供高效、規范化的數據輸入。其設計方法系統性地解決了以下關鍵問題&#xff0c;并對應發展了成熟的技術方案&#xff1a; 一、解決的問…

Ubuntu-Linux中vi / vim編輯文件,保存并退出

1.打開文件 vi / vim 文件名&#xff08;例&#xff1a; vim word.txt &#xff09;。 若權限不夠&#xff0c;則在前方添加 sudo &#xff08;例&#xff1a;sudo vim word.txt &#xff09;來增加權限&#xff1b; 2.進入文件&#xff0c;按 i 鍵進入編輯模式。 3.編輯結…

PCL繪制點云+法線

讀取的點云ASCII碼文件&#xff0c;每行6個數據&#xff0c;3維坐標3維法向 #include <iostream> #include <fstream> #include <vector> #include <string> #include <pcl/point_types.h> #include <pcl/point_cloud.h> #include <pc…