【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.mzph.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…