实战:手写一个通用Web层鉴权注解,解决水平权限漏洞

实战:手写一个通用Web层鉴权注解,解决水平权限漏洞

实战:手写一个通用Web层鉴权注解,解决水平权限漏洞

🌺The Begin🌺点点关注,收藏不迷路🌺

一、背景:一次渗透测试引发的改造

前段时间公司做渗透测试,我们系统暴露了一个典型的安全漏洞——水平权限漏洞

简单来说,就是用户A可以看到不属于他所在公司的数据。比如:用户A登录系统后,修改URL中的公司ID参数,就能查看到B公司的业务数据。

这个问题在行业内其实很常见,核心原因是Web层缺少水平鉴权。我们的系统运行好几年了,接口越来越多,但鉴权这块一直没好好做。

漏洞等级被定为高危,修复工作立刻提上议程。

二、需求分析:如何高效修复

面对几十个Controller、几百个接口,我的修复方案必须满足:

  1. 接入简单:开发人员加个注解就能搞定,不用写重复代码
  2. 灵活通用:能处理各种奇葩的入参结构(直接参数、对象属性、集合嵌套等)
  3. 兼容老代码:不能影响现有逻辑,老的接口不加注解就保持原样
  4. 可扩展:后续可能增加角色鉴权、垂直鉴权等

三、业务模型:用户-公司授权关系

先看下我们的权限模型:

has

has

User

string

userName

PK

string

nickName

UserCompany

string

userName

FK

long

companyId

FK

Company

long

companyId

PK

string

companyName

规则很简单:

  • 一个用户可以被授权访问多个公司的数据
  • 一个公司可以有多个授权用户
  • 用户只能查看他有权限的公司的数据

四、整体架构设计

鉴权注解的核心流程:

权限平台

请求处理流程

HTTP请求

Spring MVC

是否有@UserPermission?

直接执行业务方法

进入AOP切面

从Request获取用户信息

是否是Admin?

从入参提取鉴权对象

调用权限平台接口

是否有权限?

抛出权限异常

用户-公司关系服务

五、代码实现:一步一步来

5.1 注解定义

首先定义注解,通过属性来描述"要从哪里取鉴权信息":

packagecom.example.auth.annotation;importjava.lang.annotation.*;/** * 用户权限注解 * 用在Controller方法或类上,进行水平权限校验 */@Target({ElementType.TYPE,ElementType.METHOD})@Retention(RetentionPolicy.RUNTIME)@Documentedpublic@interfaceUserPermission{/** * 鉴权对象类型:单个公司还是多个公司 */AuthObjectTypeobjectType()defaultAuthObjectType.COMPANY;/** * 鉴权值类型:描述如何从入参中提取值 */AuthValueTypevalueType()defaultAuthValueType.RAW;/** * 参数索引,当valueType不是RAW时,指定从第几个参数取值 */intindex()default0;/** * 参数名称,支持多级,如 "companyInfo.companyId" */StringparamName()default"companyId";/** * 是否忽略鉴权,用于覆盖类上的注解 */booleanignore()defaultfalse;}

两个枚举的定义:

packagecom.example.auth.annotation;/** * 鉴权对象类型 */publicenumAuthObjectType{COMPANY,// 单个公司COMPANIES// 多个公司}
packagecom.example.auth.annotation;/** * 鉴权值类型:定义如何从入参中提取值 */publicenumAuthValueType{RAW,// 原始参数,直接就是companyId或companyIdsOBJECT_FIELD,// 对象的属性,如 bo.companyIdCOLLECTION_FIELD,// 集合元素的属性,如 List<Bo> 取 Bo.companyIdNESTED_FIELD,// 嵌套属性,如 bo.companyInfo.companyIdCOLLECTION_NESTED// 集合中的嵌套属性,如 bo.companyList.companyId}

5.2 权限管理服务

封装调用外部权限平台的逻辑:

packagecom.example.auth.manager;importcom.example.auth.client.UserPermissionFeignClient;importcom.example.common.exception.BizException;importcom.example.common.util.UserUtil;importlombok.RequiredArgsConstructor;importlombok.extern.slf4j.Slf4j;importorg.apache.commons.collections4.CollectionUtils;importorg.apache.commons.lang3.BooleanUtils;importorg.springframework.stereotype.Component;importjava.util.List;importjava.util.stream.Collectors;/** * 用户权限管理器 * 封装调用权限平台的逻辑 */@Slf4j@Component@RequiredArgsConstructorpublicclassUserPermissionManager{privatefinalUserPermissionFeignClient permissionClient;/** * 校验用户是否有指定公司的权限 */publicbooleancheckCompany(String userName,Long companyId){if(companyId ==null){thrownewBizException("公司ID不能为空");} log.debug("校验用户{}对公司{}的权限", userName, companyId);var result = permissionClient.checkCompany(userName, companyId);returncheckResult(result);}/** * 校验用户是否有所有指定公司的权限 */publicbooleancheckCompanies(String userName,List<Long> companyIds){if(CollectionUtils.isEmpty(companyIds)){thrownewBizException("公司ID列表不能为空");}// 先去重,减少调用次数List<Long> distinctIds = companyIds.stream().distinct().collect(Collectors.toList()); log.debug("校验用户{}对{}个公司的权限", userName, distinctIds.size());if(distinctIds.size()==1){// 单个公司走单条接口returncheckCompany(userName, distinctIds.get(0));}var result = permissionClient.checkCompanies(userName, distinctIds);returncheckResult(result);}privatebooleancheckResult(Result<Boolean> result){if(result ==null||!result.isSuccess()|| result.getData()==null){ log.error("调用权限平台失败: {}", result);thrownewBizException("权限校验服务异常");}returnBooleanUtils.isTrue(result.getData());}}

5.3 AOP切面:核心逻辑

这是最关键的代码,负责拦截请求、提取鉴权值、调用权限服务:

packagecom.example.auth.aspect;importcom.example.auth.annotation.UserPermission;importcom.example.auth.annotation.AuthObjectType;importcom.example.auth.annotation.AuthValueType;importcom.example.auth.manager.UserPermissionManager;importcom.example.auth.model.UserInfo;importcom.example.common.exception.BizException;importcom.example.common.util.UserUtil;importlombok.RequiredArgsConstructor;importlombok.extern.slf4j.Slf4j;importorg.aspectj.lang.ProceedingJoinPoint;importorg.aspectj.lang.annotation.Around;importorg.aspectj.lang.annotation.Aspect;importorg.aspectj.lang.annotation.Pointcut;importorg.aspectj.lang.reflect.MethodSignature;importorg.springframework.stereotype.Component;importorg.springframework.web.context.request.RequestContextHolder;importorg.springframework.web.context.request.ServletRequestAttributes;importjava.beans.PropertyDescriptor;importjava.lang.reflect.Method;importjava.util.Collection;importjava.util.List;importjava.util.stream.Collectors;/** * 用户权限切面 */@Slf4j@Aspect@Component@RequiredArgsConstructorpublicclassUserPermissionAspect{privatefinalUserPermissionManager permissionManager;/** * 切点:所有Controller下的public方法 */@Pointcut("execution(public * com.example.web.controller..*.*(..))")publicvoidcontrollerMethod(){}@Around("controllerMethod()")publicObjectcheckPermission(ProceedingJoinPoint joinPoint)throwsThrowable{// 1. 获取注解UserPermission annotation =getAnnotation(joinPoint);if(annotation ==null|| annotation.ignore()){// 没注解或忽略鉴权,直接放行return joinPoint.proceed();}// 2. 获取当前用户UserInfo currentUser =getCurrentUser();if(currentUser ==null){thrownewBizException("获取用户信息失败");}// 3. Admin直接放行if(UserUtil.isAdmin(currentUser.getUserName())){ log.debug("Admin用户放行");return joinPoint.proceed();}// 4. 从入参中提取鉴权值Object authValue =extractAuthValue(joinPoint, annotation);// 5. 校验权限boolean hasPermission =checkUserPermission( currentUser.getUserName(), authValue, annotation.objectType());if(!hasPermission){ log.warn("用户{}没有权限访问: {}", currentUser.getUserName(), authValue);thrownewBizException("您没有权限访问该数据");}// 6. 放行return joinPoint.proceed();}/** * 获取方法上的注解,优先取方法级,没有则取类级 */privateUserPermissiongetAnnotation(ProceedingJoinPoint joinPoint){MethodSignature signature =(MethodSignature) joinPoint.getSignature();Method method = signature.getMethod();Class<?> targetClass = signature.getDeclaringType();// 方法上的注解优先级更高UserPermission methodAnn = method.getAnnotation(UserPermission.class);if(methodAnn !=null){return methodAnn;}return targetClass.getAnnotation(UserPermission.class);}/** * 从请求中获取当前用户 */privateUserInfogetCurrentUser(){ServletRequestAttributes attrs =(ServletRequestAttributes)RequestContextHolder.getRequestAttributes();if(attrs ==null){returnnull;}return(UserInfo) attrs.getRequest().getAttribute("userInfo");}/** * 从方法参数中提取鉴权值(核心方法) */privateObjectextractAuthValue(ProceedingJoinPoint joinPoint,UserPermission annotation){MethodSignature signature =(MethodSignature) joinPoint.getSignature();String[] paramNames = signature.getParameterNames();Object[] args = joinPoint.getArgs();if(paramNames ==null|| paramNames.length ==0){thrownewBizException("方法没有参数,无法提取鉴权值");}AuthValueType valueType = annotation.valueType();// 场景1:原始参数if(valueType ==AuthValueType.RAW){returnextractRawParam(paramNames, args, annotation.paramName());}// 其他场景:需要从对象中取值int index = annotation.index();if(index <0|| index >= args.length){thrownewBizException("参数索引越界: "+ index);}Object target = args[index];if(target ==null){thrownewBizException("第"+ index +"个参数为null");}switch(valueType){caseOBJECT_FIELD:// 场景2:对象的属性,如 bo.companyIdreturngetFieldValue(target, annotation.paramName());caseCOLLECTION_FIELD:// 场景3:集合元素的属性,如 List<Bo> 取 Bo.companyIdreturngetCollectionFieldValues(target, annotation.paramName());caseNESTED_FIELD:// 场景4:嵌套属性,如 bo.companyInfo.companyIdreturngetNestedFieldValue(target, annotation.paramName());caseCOLLECTION_NESTED:// 场景5:集合中的嵌套属性,如 bo.companyList.companyIdreturngetCollectionNestedValues(target, annotation.paramName());default:thrownewBizException("不支持的取值类型: "+ valueType);}}/** * 提取原始参数 */privateObjectextractRawParam(String[] paramNames,Object[] args,String paramName){for(int i =0; i < paramNames.length; i++){if(paramName.equals(paramNames[i])){return args[i];}}thrownewBizException("未找到参数: "+ paramName);}/** * 通过反射获取对象属性值(使用getter方法) */privateObjectgetFieldValue(Object obj,String fieldName){try{PropertyDescriptor pd =newPropertyDescriptor(fieldName, obj.getClass());Method getter = pd.getReadMethod();if(getter ==null){thrownewBizException("属性 "+ fieldName +" 没有getter方法");}return getter.invoke(obj);}catch(Exception e){ log.error("获取属性值失败: {}", fieldName, e);thrownewBizException("解析参数失败: "+ fieldName);}}/** * 获取集合元素的属性值列表 */privateList<Object>getCollectionFieldValues(Object obj,String fieldName){if(!(obj instanceofCollection)){thrownewBizException("参数不是Collection类型");}Collection<?> collection =(Collection<?>) obj;return collection.stream().map(item ->getFieldValue(item, fieldName)).collect(Collectors.toList());}/** * 获取嵌套属性值,如 obj.field1.field2 */privateObjectgetNestedFieldValue(Object obj,String fieldPath){String[] fields = fieldPath.split("\\.");Object current = obj;for(String field : fields){if(current ==null){thrownewBizException("嵌套属性路径中有null值: "+ fieldPath);} current =getFieldValue(current, field);}return current;}/** * 获取集合中的嵌套属性值 */privateList<Object>getCollectionNestedValues(Object obj,String fieldPath){String[] parts = fieldPath.split("\\.");if(parts.length !=2){thrownewBizException("COLLECTION_NESTED类型需要两级路径,如: companyList.companyId");}// 第一级:获取集合属性Object collectionObj =getFieldValue(obj, parts[0]);if(!(collectionObj instanceofCollection)){thrownewBizException(parts[0]+"不是Collection类型");}// 第二级:遍历集合,获取每个元素的属性Collection<?> collection =(Collection<?>) collectionObj;return collection.stream().map(item ->getFieldValue(item, parts[1])).collect(Collectors.toList());}/** * 执行权限校验 */privatebooleancheckUserPermission(String userName,Object authValue,AuthObjectType objectType){if(objectType ==AuthObjectType.COMPANY){// 单个公司Long companyId =convertToLong(authValue);return permissionManager.checkCompany(userName, companyId);}else{// 多个公司List<Long> companyIds =convertToLongList(authValue);return permissionManager.checkCompanies(userName, companyIds);}}privateLongconvertToLong(Object value){if(value instanceofLong){return(Long) value;}if(value instanceofInteger){return((Integer) value).longValue();}if(value instanceofString){returnLong.parseLong((String) value);}thrownewBizException("无法转换为Long类型: "+ value);}@SuppressWarnings("unchecked")privateList<Long>convertToLongList(Object value){if(value instanceofCollection){return((Collection<?>) value).stream().map(this::convertToLong).collect(Collectors.toList());}thrownewBizException("无法转换为Long列表: "+ value);}}

六、使用示例

看看实际项目中怎么用这个注解:

6.1 场景1:最简单的用法

@RestController@RequestMapping("/api/app")publicclassAppController{/** * 直接参数:companyId就在参数列表里 */@GetMapping("/list")@UserPermissionpublicResult<List<AppInfo>>listApps(long companyId){// 直接使用companyId,注解自动取值returnResult.success(appService.listByCompany(companyId));}}

6.2 场景2:对象属性

@DatapublicclassAppQueryRequest{privateLong companyId;privateString appName;privateInteger pageNum;privateInteger pageSize;}@PostMapping("/query")@UserPermission( valueType =AuthValueType.OBJECT_FIELD, paramName ="companyId")publicResult<PageInfo<AppInfo>>queryApps(@RequestBodyAppQueryRequest request){// 从request.companyId取值returnResult.success(appService.queryPage(request));}

6.3 场景3:批量操作

@PostMapping("/batch/delete")@UserPermission( objectType =AuthObjectType.COMPANIES, valueType =AuthValueType.COLLECTION_FIELD, paramName ="companyId")publicResult<Void>batchDelete(@RequestBodyList<AppInfo> apps){// 从每个AppInfo对象中提取companyId,组成列表后校验// 确保用户对这些companyId都有权限 appService.batchDelete(apps);returnResult.success();}

6.4 场景4:嵌套属性

@DatapublicclassComplexRequest{privateCompanyInfo companyInfo;@DatapublicstaticclassCompanyInfo{privateLong companyId;}}@PostMapping("/complex")@UserPermission( valueType =AuthValueType.NESTED_FIELD, paramName ="companyInfo.companyId")publicResult<Object>complexOperation(@RequestBodyComplexRequest request){// 从request.companyInfo.companyId取值returnResult.success();}

6.5 场景5:类级别默认配置

@RestController@RequestMapping("/api/user")@UserPermission(valueType =AuthValueType.OBJECT_FIELD, paramName ="companyId")publicclassUserController{/** * 继承类上的注解 */@GetMapping("/list")publicResult<List<UserVO>>listUsers(@RequestParamLong companyId){returnResult.success(userService.listByCompany(companyId));}/** * 覆盖类上的注解 */@PostMapping("/batch/query")@UserPermission( objectType =AuthObjectType.COMPANIES, valueType =AuthValueType.COLLECTION_FIELD, paramName ="companyId")publicResult<List<UserVO>>batchQuery(@RequestBodyList<CompanyQuery> queries){returnResult.success(userService.batchQuery(queries));}/** * 忽略鉴权 */@GetMapping("/public/info")@UserPermission(ignore =true)publicResult<PublicInfo>getPublicInfo(){returnResult.success(userService.getPublicInfo());}}

七、遇到的坑和解决方案

坑1:参数名获取不到

问题:编译后参数名变成arg0、arg1,导致按名称取值失败。

解决:Maven编译插件添加-parameters参数:

<plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><configuration><compilerArgs><arg>-parameters</arg></compilerArgs></configuration></plugin>

坑2:循环依赖

问题:AOP切面注入UserPermissionManagerUserPermissionManager又依赖FeignClient,FeignClient可能依赖AOP,形成循环。

解决:使用@Lazy注解延迟加载:

@Aspect@Component@RequiredArgsConstructorpublicclassUserPermissionAspect{@LazyprivatefinalUserPermissionManager permissionManager;}

坑3:集合参数去重

问题:批量接口传入的companyIds可能重复,重复调用权限平台浪费资源。

解决:在UserPermissionManager中先做去重:

List<Long> distinctIds = companyIds.stream().distinct().collect(Collectors.toList());

坑4:事务失效

问题:切面中抛出异常,但业务方法的事务没有回滚。

解决:确保异常在事务切面之后抛出,调整切面顺序:

@Order(1)// 数字越小越先执行publicclassUserPermissionAspect{// ...}

八、编译期校验:把问题扼杀在摇篮里

注解用起来很方便,但也很容易配错。比如COLLECTION_FIELD必须搭配COMPANIES使用,如果配成COMPANY,运行时就会出错。

我们可以用注解处理器在编译期就发现这些问题:

8.1 创建注解处理器

packagecom.example.auth.processor;importcom.example.auth.annotation.UserPermission;importcom.example.auth.annotation.AuthObjectType;importcom.example.auth.annotation.AuthValueType;importjavax.annotation.processing.*;importjavax.lang.model.SourceVersion;importjavax.lang.model.element.*;importjavax.lang.model.type.TypeMirror;importjavax.tools.Diagnostic;importjava.util.Set;/** * UserPermission注解处理器 * 编译期校验注解配置是否正确 */@SupportedAnnotationTypes("com.example.auth.annotation.UserPermission")@SupportedSourceVersion(SourceVersion.RELEASE_8)publicclassUserPermissionProcessorextendsAbstractProcessor{@Overridepublicbooleanprocess(Set<?extendsTypeElement> annotations,RoundEnvironment roundEnv){for(Element element : roundEnv.getElementsAnnotatedWith(UserPermission.class)){checkAnnotation(element);}returntrue;}privatevoidcheckAnnotation(Element element){UserPermission annotation = element.getAnnotation(UserPermission.class);// 规则1:COLLECTION_FIELD必须搭配COMPANIESif(annotation.valueType()==AuthValueType.COLLECTION_FIELD&& annotation.objectType()!=AuthObjectType.COMPANIES){ processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR,"当valueType=COLLECTION_FIELD时,objectType必须是COMPANIES", element );}// 规则2:COLLECTION_NESTED必须搭配COMPANIESif(annotation.valueType()==AuthValueType.COLLECTION_NESTED&& annotation.objectType()!=AuthObjectType.COMPANIES){ processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR,"当valueType=COLLECTION_NESTED时,objectType必须是COMPANIES", element );}// 规则3:index不能小于0if(annotation.index()<0){ processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR,"index不能小于0", element );}// 规则4:RAW类型时,paramName必须存在if(annotation.valueType()==AuthValueType.RAW&&(annotation.paramName()==null|| annotation.paramName().isEmpty())){ processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR,"RAW类型时,paramName不能为空", element );}}}

8.2 注册处理器

resources/META-INF/services/javax.annotation.processing.Processor文件中:

com.example.auth.processor.UserPermissionProcessor 

配置好后,如果写错注解,IDE会直接报红:

@UserPermission( valueType =AuthValueType.COLLECTION_FIELD,// 编译错误! objectType =AuthObjectType.COMPANY// 应该用COMPANIES)publicResult<?>badMethod(){// ...}

九、性能优化建议

随着接入的应用越来越多,有几个性能点需要注意:

9.1 缓存权限结果

权限关系相对稳定,可以加一层缓存:

@ComponentpublicclassCachedUserPermissionManagerextendsUserPermissionManager{privatefinalCache<String,Boolean> permissionCache =Caffeine.newBuilder().expireAfterWrite(5,TimeUnit.MINUTES).maximumSize(10000).build();@OverridepublicbooleancheckCompany(String userName,Long companyId){String key = userName +":"+ companyId;return permissionCache.get(key, k ->super.checkCompany(userName, companyId));}}

9.2 批量接口合并请求

对于checkCompanies接口,可以合并同一用户的多次请求:

// 使用异步批量处理publicCompletableFuture<Boolean>checkCompaniesAsync(String userName,List<Long> companyIds){// 合并请求,批量调用}

9.3 反射优化

反射获取属性值有一定开销,可以考虑缓存PropertyDescriptor:

@ComponentpublicclassFieldReader{privatefinalConcurrentMap<String,PropertyDescriptor> cache =newConcurrentHashMap<>();publicObjectreadField(Object obj,String fieldName){Class<?> clazz = obj.getClass();String key = clazz.getName()+"#"+ fieldName;PropertyDescriptor pd = cache.computeIfAbsent(key, k ->{try{returnnewPropertyDescriptor(fieldName, clazz);}catch(IntrospectionException e){thrownewRuntimeException(e);}});try{return pd.getReadMethod().invoke(obj);}catch(Exception e){thrownewRuntimeException(e);}}}

十、总结与展望

通过这次改造,我们实现了:

  1. ✅ 统一鉴权:所有接口都用同一套注解,规范统一
  2. ✅ 接入简单:开发人员只需要加注解,不用写重复代码
  3. ✅ 灵活通用:支持5种常见的取值场景,覆盖95%以上的接口
  4. ✅ 安全可靠:编译期校验+运行时检查,双重保障

目前这个注解已经在我们的核心业务上线,覆盖了200+接口。后续还可以扩展:

  • 支持角色鉴权:增加@RolePermission注解
  • 支持数据脱敏:结合注解实现字段级脱敏
  • 支持操作审计:自动记录谁在什么时间操作了什么数据
  • 做成Starter:封装成Spring Boot Starter,供其他项目复用

如果觉得文章有帮助,欢迎点赞收藏。有问题可以在评论区交流,我会尽量回复。

在这里插入图片描述

🌺The End🌺点点关注,收藏不迷路🌺

Read more

【C++ Qt】网络编程(QUdpSocket、QTcpSocket、Http)

【C++ Qt】网络编程(QUdpSocket、QTcpSocket、Http)

每日激励:“不设限和自我肯定的心态:I can do all things。 — Stephen Curry” 绪论 : 本章将提到Qt中的网络部分,在看这篇文章之前需要有一定的网络基础也就是TCP/HTTP、本篇文章主要讲到的是Qt中基础的Udp、Tcp、Http的使用方法,并附有了多个小demo方便实操练习,并且其中还在每章最后进行了小总结回顾重要接口和函数方便回顾。 ———————— 早关注不迷路,话不多说安全带系好,发车啦(建议电脑观看)。 网络编程主要依赖于操作系统提供的Socket API。需要注意的是,C++标准库本身并未封装网络编程相关的API。 关于Qt网络编程的几个要点: 1. 网络应用开发本质上是编写应用层代码,需要传输层协议(如TCP/UDP)的支持 2. 为此,Qt提供了两套专门的网络编程API(QUDPSocket和QTcpSocket) 3. 使用Qt网络编程API时,需先在.pro文件中添加network模块 4. 之前学习的Qt控件和核心功能都属于QtCore模块(默认已包含) 为什么Qt要划分出这些模块呢? Qt 本身是一个非常庞

By Ne0inhk
C++ 波澜壮阔 40 年:引用、内联函数与现代空指针,效率跃升三基石

C++ 波澜壮阔 40 年:引用、内联函数与现代空指针,效率跃升三基石

🔥@晨非辰Tong: 个人主页 👀专栏:《数据结构与算法入门指南》、《C++学习之旅》 💪学习阶段:C语言、数据结构与算法初学者 ⏳“人理解迭代,神理解递归。” 文章目录 * 引言 * 一、引用:C++前期重难点 * 1.1 一览:引用的方方面面 * 1.2 划重点:引用的正确使用 * 1.3 存疑的地方 * 1.4 const引用 * 1.5 引用和指针的关系(面试必看) * 二、inline内联函数 * 2.1 对要点的详细解释 * 三、宏:nullptr * 总结 引言 C++的演进之路,是不断在性能与安全、灵活与严谨之间寻求平衡的艺术。 本文将深入剖析三大特性:

By Ne0inhk
SkyWalking - .NET / C++ / Lua 探针现状与社区支持

SkyWalking - .NET / C++ / Lua 探针现状与社区支持

👋 大家好,欢迎来到我的技术博客! 📚 在这里,我会分享学习笔记、实战经验与技术思考,力求用简单的方式讲清楚复杂的问题。 🎯 本文将围绕SkyWalking这个话题展开,希望能为你带来一些启发或实用的参考。 🌱 无论你是刚入门的新手,还是正在进阶的开发者,希望你都能有所收获! 文章目录 * SkyWalking - .NET / C++ / Lua 探针现状与社区支持 🌐 * 一、SkyWalking 多语言探针架构概览 🧩 * 二、Java 探针:成熟稳定,功能最全 ☕️ * 示例:Spring Boot 应用接入 SkyWalking * Java 探针高级特性 * 三、.NET 探针现状:渐趋成熟,生产可用 🖥️ * 技术原理 * 使用方式 * 当前支持的功能 * 局限性 * 四、C++ 探针现状:SDK 形式,适合嵌入式场景 ⚙️ * cpp2sky SDK

By Ne0inhk

C++ 基础语法完全入门指南 - 针对零基础小白

第一部分:C++是什么?为什么学习它? 1.1 C++简介 C++就像是一种与计算机对话的语言,它: * 高效快速:直接操作计算机硬件,运行速度快 * 功能强大:可以用来开发游戏、操作系统、科学计算等 * 学习曲线:开始有点难,但掌握后非常强大 1.2 第一个C++程序 - “Hello World” #include<iostream>// 包含输入输出库intmain(){// 主函数,程序从这里开始执行 std::cout <<"Hello, World!"<< std::endl;// 输出文字到屏幕return0;// 程序正常结束} 逐行解释:

By Ne0inhk