网站建设需要学的北京高端网站建设规划

张小明 2025/12/27 9:32:35
网站建设需要学的,北京高端网站建设规划,做微博分析的网站,网站怎么做电子合同背景 目前我负责的一个公司内部Java应用#xff0c;其Web层几乎没有进行水平鉴权#xff0c;存在着一定的风险#xff0c;比如A可以看到不属于他的B公司的数据。最近公司进行渗透测试#xff0c;将这个风险暴露出来#xff0c;并将修复提上了议程。 由于Web层的接口很多…背景目前我负责的一个公司内部Java应用其Web层几乎没有进行水平鉴权存在着一定的风险比如A可以看到不属于他的B公司的数据。最近公司进行渗透测试将这个风险暴露出来并将修复提上了议程。由于Web层的接口很多我希望能用一种较为通用易于接入的方式来完成这个工作。很容易就想到了通过注解方式进行水平鉴权。说来惭愧我工作了十年多还没有从0到1写一个稍微复杂点的注解正好利用这个机会进行学习和实践。我结合了一些现有代码以及DeepSeek元宝版的建议实现了相关功能。为了便于理解本文在保留适用场景通用性的前提下删减无关的代码。鉴权场景一个公司可以给一个或多个用户授权一个用户可以被一个或多个公司授权。用户只能查看被授权公司的数据。架构实现注解定义/* by 01130.hk - online tools website : 01130.hk/zh/quchong.html */ Target({ElementType.TYPE, ElementType.METHOD}) Retention(RetentionPolicy.RUNTIME) Documented public interface UserPermission { /** 鉴权对象类型如果是List应使用COMPANIES */ AuthObjectTypeEnum objectType() default AuthObjectTypeEnum.COMPANY; /** 鉴权对象值类型定义如何取得用来鉴权的对象 */ AuthObjectValueEnum valueType() default AuthObjectValueEnum.RARE; /** 鉴权参数序号. 0~n表示第x个参数对象的内部成员变量仅当 valueType不为RARE时有效 */ int index() default 0; /** 鉴权参数名称, 如果是多级的使用.分割比如companyInfo.companyId */ String paramName() default companyId; /** 是否忽略鉴权用于覆盖类上有默认注解的场景此时接口不需要鉴权 */ boolean isIgnore() default false; }两个枚举的取值范围是/* by 01130.hk - online tools website : 01130.hk/zh/quchong.html */ /** * 授权对象类型 * */ Getter public enum AuthObjectTypeEnum { COMPANY(1, 单个企业), COMPANIES(2, 多个企业); private final int value; private final String des; AuthObjectTypeEnum(int value, String des) { this.value value; this.des des; } }/** * 授权对象参数类型即该参数是以何种形式出现在形参表的 * */ Getter public enum AuthObjectValueEnum { /** 参数直接按原始值出现 */ RARE(1, 原始参数), /** eg1. 入参是A,取A.companyId, eg2. 入参是B取B.companyIds */ OBJECT_FIELD(2, 对象的属性), /** eg. 入参是ListA objects,取A.companyId */ COLLECTION_FIELD(3, 集合元素的属性), /** eg. 入参是A取A.companyInfo.companyId, A.B.companyIds */ OBJECT_SUB_FIELD(4, 对象的属性的属性), /** eg. 入参是A取A.companyList中的companyId */ OBJECT_COLLECTION_FIELD(5, 对象的属性中集合元素的属性), ; private final int value; private final String des; AuthObjectValueEnum(int value, String des) { this.value value; this.des des; } }鉴权辅助类调用外部服务本例是公司-用户关系管理平台获取一个用户是否有单个或多个公司的权限。/** * 公司-用户关系管理平台 用户鉴权服务封装 * */ Component public class UserPermissionManager { Resource private UserPermissionFeignService userPermissionFeignService; /** * 检测用户是否对公司的数据拥有访问权限 * * param userName * param companyId * return */ public boolean checkCompany(String userName, long companyId) { return checkResult(userPermissionFeignService.checkCompany(userName, companyId)); } /** * 批量检测用户是否对所有指定的公司的数据都拥有访问权限 * * param userName * param companyIds * return */ public boolean checkCompanies(String userName, ListLong companyIds) { if (CollectionUtils.isEmpty(companyIds)) { throw new BizException(鉴权公司id列表不能为空); } // 去重 companyIds companyIds.stream().distinct().collect(Collectors.toList()); return companyIds.size() 1 ? checkResult(userPermissionFeignService.checkCompany(userName, companyIds.get(0))) : checkResult(userPermissionFeignService.checkCompanies(userName, companyIds)); } private boolean checkResult(ResultVOBoolean result) { if (result null || result.getCode() ! HttpCodeConstants.SUCCESS_CODE || result.getData() null) { throw new BizException(调用平台进行用户企业数据权限鉴权失败); } return BooleanUtils.isTrue(result.getData()); } }AOP切面鉴权功能的核心注解只有在定义了对应的处理切面后才能发挥作用。Aspect Component public class UserPermissionAspect { Resource UserPermissionManager userPermissionManager; /** 定义切点 */ Pointcut(execution(public * com.yourcompany.xxproject.web.controller..*.*(..))) public void privilege() {} Around(privilege()) public Object isAccessMethod(ProceedingJoinPoint joinPoint) throws Throwable { // 获取方法签名和注解信息 MethodSignature signature (MethodSignature) joinPoint.getSignature(); Method method signature.getMethod(); Class targetClass signature.getDeclaringType(); UserPermission permissionOnClass (UserPermission) targetClass.getAnnotation(UserPermission.class); UserPermission permissionOnMethod method.getAnnotation(UserPermission.class); // 优先取方法上的注解 UserPermission permissionAnnotation permissionOnClass ! null ? permissionOnClass : permissionOnMethod; if (permissionAnnotation null || permissionAnnotation.isIgnore()) { // 如果没有注解或注解的策略是忽略直接放行 return joinPoint.proceed(); } ServletRequestAttributes servletRequestAttributes (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); if (servletRequestAttributes null) { throw new BizException(请求中缺少属性信息); } UserInfoVO userVo (UserInfoVO) servletRequestAttributes.getRequest().getAttribute(userVo); if (userVo null || StringUtils.isBlank(userVo.getUserName())) { throw new BizException(请求中缺少用户信息或不完整); } String userName userVo.getUserName(); // admin直接放行 if (UserUtil.isAdminOrSystem(userVo.getUserName())) { return joinPoint.proceed(); } try { Object authValue extractAuthValue(joinPoint, permissionAnnotation); boolean hasPermission checkUserPermission(userName, authValue, permissionAnnotation.objectType()); if (!hasPermission) { LoggerUtils.error( String.format( 用户%s没有%s类型对象%s的访问权限, 拒绝访问, userName, permissionAnnotation.objectType().getDes(), authValue)); throw new BizException(权限不足无法访问对应的数据。请确认当前用户是待访问数据所属的企业/企业组的成员。); } } catch (BizException e) { throw e; } catch (Exception e) { LoggerUtils.error(权限校验失败发生异常, e); throw new BizException(权限校验失败发生异常, e); } return joinPoint.proceed(); } /** * 从方法参数中提取鉴权对象的值 * * param joinPoint * param annotation * return */ private Object extractAuthValue(ProceedingJoinPoint joinPoint, UserPermission annotation) { MethodSignature signature (MethodSignature) joinPoint.getSignature(); String[] parameterNames signature.getParameterNames(); Object[] args joinPoint.getArgs(); if (parameterNames null || parameterNames.length 0 || args null || args.length 0) { throw new BizException(用户数据鉴权解析参数时调用方法参数表为空); } if (annotation.valueType().getValue() AuthObjectValueEnum.RARE.getValue()) { // 直接按名称获取 for (int i 0; i parameterNames.length; i) { if (StringUtils.equals(parameterNames[i], annotation.paramName())) { return args[i]; } } throw new BizException(用户数据鉴权解析参数时未在形参表找到指定的鉴权参数); } // 参数所在的参数表的位置 int index annotation.index(); // 其他情况从入参对象获取 if (index 0 || index args.length) { throw new BizException(用户数据鉴权解析参数时索引越界: index 参数总数: args.length); } Object targetParam args[index]; if (targetParam null) { throw new BizException(用户数据鉴权解析参数时第 index 个参数为null); } if (annotation.valueType().getValue() AuthObjectValueEnum.OBJECT_FIELD.getValue()) { // 从对象属性中获取, 比如A.companyId、A.companyIds return extractFieldValue(targetParam, annotation.paramName()); } else if (annotation.valueType().getValue() AuthObjectValueEnum.COLLECTION_FIELD.getValue()) { // 从集合对象的属性中获取比如ListA取A.companyId // 此时必然是一个List不考虑去重 return extractListFieldValue(targetParam, annotation.paramName()); } else if (annotation.valueType().getValue() AuthObjectValueEnum.OBJECT_SUB_FIELD.getValue()) { // 从对象的属性的属性获取比如A.companyInfo.companyId String[] fieldNames annotation.paramName().split(\\.); Object subTargetParam extractFieldValue(targetParam, fieldNames[0]); return extractFieldValue(subTargetParam, fieldNames[1]); } else if (annotation.valueType().getValue() AuthObjectValueEnum.OBJECT_COLLECTION_FIELD.getValue()) { // 从对象的集合属性中获取比如A.companyList, 取companyList.companyId String[] fieldNames annotation.paramName().split(\\.); Object subTargetParam extractFieldValue(targetParam, fieldNames[0]); return extractListFieldValue(subTargetParam, fieldNames[1]); } else { throw new BizException(用户数据鉴权解析参数, 参数值类型配置错误); } } /** * 实际的鉴权 * * param authValue * param authObjectTypeEnum * return */ private boolean checkUserPermission( String userName, Object authValue, AuthObjectTypeEnum authObjectTypeEnum) { if (authObjectTypeEnum.getValue() AuthObjectTypeEnum.COMPANY.getValue()) { return userPermissionManager.checkCompany(userName, getLongValue(authValue)); } else if (authObjectTypeEnum.getValue() AuthObjectTypeEnum.COMPANIES.getValue()) { return userPermissionManager.checkCompanies(userName, getLongListValue(authValue)); } else { throw new BizException(按用户维度鉴权该接口的鉴权对象类型非法); } } private static long getLongValue(Object authValue) { if (authValue instanceof Long) { return (long) authValue; } else if (authValue instanceof Integer) { return (int) authValue; } else if (authValue instanceof String) { return Long.parseLong((String) authValue); } else { throw new BizException(按用户维度鉴权企业id不是可处理的类型); } } private static ListLong getLongListValue(Object authValue) { if (!(authValue instanceof Collection)) { throw new BizException(按用户维度鉴权企业id列表不是Collection类型); } Collection? col (Collection?) authValue; return col.stream().map(UserPermissionAspect::getLongValue).collect(Collectors.toList()); } /** * 通过反射从对象中提取字段值 * * param targetObject * param fieldName */ private Object extractFieldValue(Object targetObject, String fieldName) { if (targetObject null || fieldName null || fieldName.isEmpty()) { throw new IllegalArgumentException(目标对象和字段名不能为空); } try { PropertyDescriptor pd new PropertyDescriptor(fieldName, targetObject.getClass()); Method getter pd.getReadMethod(); if (getter null) { throw new BizException(字段 fieldName 没有对应的getter方法); } return getter.invoke(targetObject); } catch (IntrospectionException e) { // 如果找不到标准Getter不尝试直接访问字段即field.setAccessible(true); field.get(targetObject); // 此注解处理的都是Controller入参不太可能没有Getter/Setter没必要写冗余的处理逻辑 throw new BizException(调用字段 fieldName 没有找到标准getter方法时发生错误, e); } catch (IllegalAccessException | InvocationTargetException e) { throw new BizException(调用字段 fieldName 的getter方法时发生错误, e); } } private ListLong extractListFieldValue(Object targetParam, String fieldName) { if (!(targetParam instanceof Collection)) { throw new BizException(按用户维度鉴权待处理对象列表不是Collection类型); } Collection? col (Collection?) targetParam; ListLong result Lists.newArrayList(); for (Object obj : col) { if (obj null) { throw new BizException(按用户维度鉴权集合中包含null元素无法访问其字段。); } result.add(getLongValue(extractFieldValue(obj, fieldName))); } return result; } }设计思路目前的处理逻辑并不是第一版和其他功能一样也是从最基础的功能开始一点一点往上加的。简述一下设计思路最常见的场景使用方式最简单。最常见的场景是鉴权所需参数本例是公司id即companyId直接出现在方法的形参表里。这种情况只需要直接给方法打UserPermission注解。尽量减少重复配置。如果一个Controller类的大部分入参形式是一样的那么直接在Controller类上打注解。预处理重复参数。多个comanyId鉴权时在调用外部服务前先做去重可能可以减少服务提供方的系统开销。当然这里的系统提供方的代码也是我写的我会在提供方代码里也做一次去重。直接放行的场景处理。如admin用户不需要做任何权限配置就可以访问这块逻辑可以放在切面也可以放在UserPermissionManger对象取值优先使用getter。Web层的Controller入参对象一般都是pojo直接调用getter效率更好。如果没有pojo那就需要使用反射方法来直接读取属性值。使用示例不同场景下注解属性字段的配置方式如下表场景名称和实例objectTypevalueTypeindexparamNameisIgnore首个参数且参数名称为默认名称func(long companyId)默认(COMPANY)默认(RARE)默认(0)默认(companyId)默认(false)首个参数id列表func(List companyIds)COMPANIESRARE默认(0)companyIds默认(false)首个参数的属性func(TaBO bo)bo.companyId默认(COMPANY)OBJECT_FIELD默认(0)默认(companyId)默认(false)首个参数的属性func(TbBO bo)bo.companyIdsCOMPANIESOBJECT_FIELD默认(0)companyIds默认(false)第二个参数的属性func(TaBO abo, TbBO bbo)bbo.companyId默认(COMPANY)OBJECT_FIELD1默认(companyId)默认(false)首个集合参数的属性func(List list)abo.companyIdCOMPANIESCOLLECTION_FIELD默认(0)默认(companyId)默认(false)首个参数属性的属性func(TaBO abo)abo.companyInfo.companyId默认(COMPANY)OBJECT_SUB_FIELD默认(0)companyInfo.companyId默认(false)首个参数的属性是一个集合这个集合元素的属性func(TaBO abo)abo.companyInfoListcompanyInfo.companyIdCOMPANIESOBJECT_COLLECTION_FIELD默认(0)companyInfoList.companyId默认(false)Controller类本身有鉴权注解但是当前方法不需要鉴权任意值任意值任意值任意值true边界场景处理看起来这个注解已经非常全面可以能处理绝大多数场景了是吗话不能说绝对在一个运行多年的系统中你会看到这种入参Task queryTask(Long taskId)companyId字段是Task的属性现在注解是不是束手无策了有两个选择进一步扩展注解的适用场景或者为这种场景编写专属的注解直接编码先查Task取companyId手动调用userPermissionManger我选用了方案二。方案一固然可以将更多的接口接入转化为注解但是也要根据实际情况如果适用的接口并不多新增的注解相关代码也意味着额外的维护成本。编译期校验根据注解的使用实例可以看到objectType和valueType是有一定的对应关系的valueTypeCOLLECTION_FIELD或OBJECT_COLLECTION_FIELD时objectType必然是COMPANIES。如果写错了也没测试出来上线以后还要重新改可不是什么好事。此外index显然是大于等于0的。如何将这些限制的检查放到编译期从根源上防止配置错误呢方法是有的而且有好几种。但是有些方法需要增加三方库依赖及对应的配置我选择了一个最简单的方案利用JDK8的原生能力就能完成不过缺点是需要把注解相关代码移到业务代码之外的module并且确保这个module比业务module先编译。步骤如下首先将你的注解相关代码切面代码除外移到业务层之外的module。不推荐放到对外打包的module因此我选择新建了一个。注意处理各个module的依赖确保业务层依赖了这个module编写注解处理器代码对注解的配置值进行校验/** * UserPermission注解处理器在编译期校验注解是否配置正确 * * 如果和业务代码在同一模块需要做很多额外的编译配置或引入二方包并配置。简单起见单独抽一个模块 * */ SupportedAnnotationTypes(com.yourcompany.xxproject.annotation.UserPermission) SupportedSourceVersion(SourceVersion.RELEASE_8) public class UserPermissionProcessor extends AbstractProcessor { Override public boolean process(Set? extends TypeElement annotations, RoundEnvironment roundEnv) { for (TypeElement annotation : annotations) { Set? extends Element annotatedElements roundEnv.getElementsAnnotatedWith(annotation); for (Element element : annotatedElements) { checkUserPermissionUsage(element); } } return true; } private void checkUserPermissionUsage(Element element) { AnnotationMirror userPermissionMirror getAnnotationMirror(element, com.yourcompany.xxproject.annotation.UserPermission); if (userPermissionMirror null) { return; } Map? extends ExecutableElement, ? extends AnnotationValue elementValues processingEnv.getElementUtils().getElementValuesWithDefaults(userPermissionMirror); checkValueTypeObjectTypeConstraint(element, userPermissionMirror, elementValues); checkIndexConstraint(element, userPermissionMirror, elementValues); } private void checkValueTypeObjectTypeConstraint( Element element, AnnotationMirror annotationMirror, Map? extends ExecutableElement, ? extends AnnotationValue values) { AuthObjectTypeEnum objectType extractEnumValue(values, objectType, element, annotationMirror); if (objectType null) { processingEnv .getMessager() .printMessage(Diagnostic.Kind.ERROR, objectType为空, element, annotationMirror); return; } AuthObjectValueEnum valueType extractEnumValue(values, valueType, element, annotationMirror); if (valueType null) { processingEnv .getMessager() .printMessage(Diagnostic.Kind.ERROR, valueType为空, element, annotationMirror); return; } if ((valueType.getValue() AuthObjectValueEnum.COLLECTION_FIELD.getValue() || valueType.getValue() AuthObjectValueEnum.OBJECT_COLLECTION_FIELD.getValue()) objectType.getValue() ! AuthObjectTypeEnum.COMPANIES.getValue()) { processingEnv .getMessager() .printMessage( Diagnostic.Kind.ERROR, 当valueType为AuthObjectValueEnum.COLLECTION_FIELD或AuthObjectValueEnum.OBJECT_COLLECTION_FIELD时 objectType必须是AuthObjectTypeEnum.COMPANIES, element, annotationMirror); } } private void checkIndexConstraint( Element element, AnnotationMirror annotationMirror, Map? extends ExecutableElement, ? extends AnnotationValue values) { AnnotationValue indexValue getAnnotationValue(values, index); if (indexValue ! null) { int indexVal (Integer) indexValue.getValue(); if (indexVal 0) { processingEnv .getMessager() .printMessage( Diagnostic.Kind.ERROR, index必须大于等于0当前值: indexVal, element, annotationMirror); } } } private AnnotationMirror getAnnotationMirror(Element element, String annotationClassName) { for (AnnotationMirror mirror : element.getAnnotationMirrors()) { if (mirror.getAnnotationType().toString().equals(annotationClassName)) { return mirror; } } return null; } private T T extractEnumValue( Map? extends ExecutableElement, ? extends AnnotationValue values, String valueKey, Element element, AnnotationMirror annotationMirror) { AnnotationValue value getAnnotationValue(values, valueKey); if (value null) { return null; } String valueStr value.toString(); try { // 1. 剔除前缀 java: trim去空格 String enumFullStr valueStr.replace(java: , ).trim(); // 2. 按最后一个.拆分枚举类名和常量名 int lastDotIndex enumFullStr.lastIndexOf(.); if (lastDotIndex -1) { throw new IllegalArgumentException(枚举字符串格式异常 valueStr); } // 枚举类全限定名如com.yourcompany.xxproject.api.annotation.enums.AuthObjectTypeEnum String enumClassName enumFullStr.substring(0, lastDotIndex); // 枚举常量名如COMPANIES String enumConstantName enumFullStr.substring(lastDotIndex 1); // 步骤3反射还原枚举实例 Class? enumClass Class.forName(enumClassName); if (enumClass.isEnum()) { return (T) Enum.valueOf((Class? extends Enum) enumClass, enumConstantName); } else { processingEnv .getMessager() .printMessage(Diagnostic.Kind.ERROR, valueKey 的值不是枚举, element, annotationMirror); } } catch (Exception e) { processingEnv .getMessager() .printMessage( Diagnostic.Kind.ERROR, valueKey 解析枚举失败原始值: valueStr, element, annotationMirror); } return null; } private AnnotationValue getAnnotationValue( Map? extends ExecutableElement, ? extends AnnotationValue values, String key) { for (Map.Entry? extends ExecutableElement, ? extends AnnotationValue entry : values.entrySet()) { if (entry.getKey().getSimpleName().toString().equals(key)) { return entry.getValue(); } } return null; } }在切面所在的module的src/main/resources/META-INF/services路径下增加一个文件javax.annotation.processing.Processor内容注解处理器的完整类路径com.yourcompany.xxproject.annotation.processor.UserPermissionProcessor如图验证写一段违反规则的代码IDE build时提示继续迭代...COMPANY和COMPANIES拼写接近如果用代码自动补全有可能写错。你可以将后者字面值改为MULTI_COMPANIES。可以将对象鉴权和角色鉴权结合起来判断用户是否有某个角色的权限也即在水平鉴权的基础上增加垂直鉴权。当然你也可以用另一个注解来完成。如果这个注解足够通用你可以将它们包括UserPermissionManger打成一个二方包供其他应用使用。这时你需要研究下如何封装远程调用相关的代码这和你所用的微服务框架有关。实际上我前司的鉴权平台就有这样一个鉴权二方包。当你把它打成了二方包就意味着使用者会日趋变多调用量也会随着业务不断增长不同的应用可能使用了这个包的不同版本一定要在早期开始优化性能作者五岳出处http://www.cnblogs.com/wuyuegb2312对于标题未标注为“转载”的文章均为原创其版权归作者所有欢迎转载但未经作者同意必须保留此段声明且在文章页面明显位置给出原文连接否则保留追究法律责任的权利。
版权声明:本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!

做网站注册页面成都网站建设scyiyou

还在为训练DeepSeek-V3时频繁出现的内存溢出而苦恼?是否尝试了各种批次大小配置,却始终无法在训练效率与稳定性之间找到最佳平衡点?本文将带你深入探索DeepSeek-V3训练过程中的性能瓶颈排查方法,通过场景化配置策略实现训练性能的…

张小明 2025/12/27 9:32:03 网站建设

竞价可以做两个网站吗白帽seo和黑帽seo

个人简介慕婉学姐精通Java、PHP、微信小程序、Python、Golang和安卓开发等语言,擅长开发大数据、深度学习、网站、小程序、安卓应用和算法项目。平时从事项目定制开发、代码讲解、答辩教学和文档编写,也掌握一些降重技巧。感谢大家的持续关注&#xff01…

张小明 2025/12/27 9:30:57 网站建设

做国内学历公证的网站竞价推广开户电话

若把“红绿黄灯”从交通信号升维成人机协同的“关系隐喻”,是一种动态权责协议。谁在当下握有“主导权”?谁该进入“观望”?谁准备“交权”? 1. 红灯——人类主权态隐喻:把机器“捆在红灯柱上”,它可预警&a…

张小明 2025/12/27 9:30:24 网站建设

响应式网站宽度cdn中国设计网

对大学生而言,课程论文堪称 “常态化难题”:既要紧跟课程主题、符合学术规范,又要在有限时间内完成选题、文献梳理、正文撰写,还要规避重复率超标风险🤯!很多同学要么拖延到截止日前熬夜拼凑,要…

张小明 2025/12/27 9:29:52 网站建设

成都捕鱼网站建设wordpress 位置

宽带网络连接指南 在当今数字化时代,高速稳定的网络连接对于我们的工作和生活至关重要。本文将详细介绍电缆调制解调器和DSL调制解调器的工作原理、配置过程以及不同类型的DSL服务,帮助你轻松实现高速上网。 电缆调制解调器的注册与使用 要使用电缆调制解调器连接互联网,…

张小明 2025/12/27 9:29:19 网站建设

石家庄市市政建设总公司网站网站流量被黑

5分钟搭建专业级文本对比系统:diff-match-patch实战全解析 【免费下载链接】diff-match-patch 项目地址: https://gitcode.com/gh_mirrors/di/diff-match-patch 在日常开发中,你是否经常遇到这样的困扰:用户反馈文档被修改了&#xf…

张小明 2025/12/27 9:28:46 网站建设