mirror of
				https://github.com/continew-org/continew-admin.git
				synced 2025-10-31 22:57:17 +08:00 
			
		
		
		
	feat: 新增接口文档配置,支持显示 SaToken 权限码 @dom-w
This commit is contained in:
		| @@ -0,0 +1,216 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *     http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | package top.continew.admin.common.config.doc; | ||||||
|  |  | ||||||
|  | import cn.dev33.satoken.annotation.SaIgnore; | ||||||
|  | import cn.hutool.core.map.MapUtil; | ||||||
|  | import io.swagger.v3.oas.models.Components; | ||||||
|  | import io.swagger.v3.oas.models.OpenAPI; | ||||||
|  | import io.swagger.v3.oas.models.PathItem; | ||||||
|  | import io.swagger.v3.oas.models.security.SecurityRequirement; | ||||||
|  | import io.swagger.v3.oas.models.security.SecurityScheme; | ||||||
|  | import lombok.RequiredArgsConstructor; | ||||||
|  | import lombok.extern.slf4j.Slf4j; | ||||||
|  | import org.springdoc.core.customizers.GlobalOpenApiCustomizer; | ||||||
|  | import org.springframework.aop.support.AopUtils; | ||||||
|  | import org.springframework.context.ApplicationContext; | ||||||
|  | import org.springframework.stereotype.Component; | ||||||
|  | import org.springframework.util.AntPathMatcher; | ||||||
|  | import org.springframework.web.bind.annotation.*; | ||||||
|  | import top.continew.starter.apidoc.autoconfigure.SpringDocExtensionProperties; | ||||||
|  | import top.continew.starter.auth.satoken.autoconfigure.SaTokenExtensionProperties; | ||||||
|  | import top.continew.starter.extension.crud.annotation.CrudRequestMapping; | ||||||
|  |  | ||||||
|  | import java.lang.reflect.Method; | ||||||
|  | import java.util.*; | ||||||
|  | import java.util.stream.Collectors; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * 全局鉴权参数定制器 | ||||||
|  |  * | ||||||
|  |  * @author echo | ||||||
|  |  * @since 2024/12/31 13:36 | ||||||
|  |  */ | ||||||
|  | @Slf4j | ||||||
|  | @Component | ||||||
|  | @RequiredArgsConstructor | ||||||
|  | public class GlobalAuthenticationCustomizer implements GlobalOpenApiCustomizer { | ||||||
|  |  | ||||||
|  |     private final SpringDocExtensionProperties properties; | ||||||
|  |     private final SaTokenExtensionProperties saTokenExtensionProperties; | ||||||
|  |     private final ApplicationContext context; | ||||||
|  |     private final AntPathMatcher pathMatcher = new AntPathMatcher(); | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 定制 OpenAPI 文档 | ||||||
|  |      * | ||||||
|  |      * @param openApi 当前 OpenAPI 对象 | ||||||
|  |      */ | ||||||
|  |     @Override | ||||||
|  |     public void customise(OpenAPI openApi) { | ||||||
|  |         if (MapUtil.isEmpty(openApi.getPaths())) { | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         // 收集需要排除的路径(包括 Sa-Token 配置中的排除路径和 @SaIgnore 注解路径) | ||||||
|  |         Set<String> excludedPaths = collectExcludedPaths(); | ||||||
|  |  | ||||||
|  |         // 遍历所有路径,为需要鉴权的路径添加安全认证配置 | ||||||
|  |         openApi.getPaths().forEach((path, pathItem) -> { | ||||||
|  |             if (isPathExcluded(path, excludedPaths)) { | ||||||
|  |                 // 路径在排除列表中,跳过处理 | ||||||
|  |                 return; | ||||||
|  |             } | ||||||
|  |             // 为路径添加安全认证参数 | ||||||
|  |             addAuthenticationParameters(pathItem); | ||||||
|  |         }); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 收集所有需要排除的路径 | ||||||
|  |      * | ||||||
|  |      * @return 排除路径集合 | ||||||
|  |      */ | ||||||
|  |     private Set<String> collectExcludedPaths() { | ||||||
|  |         Set<String> excludedPaths = new HashSet<>(); | ||||||
|  |         excludedPaths.addAll(Arrays.asList(saTokenExtensionProperties.getSecurity().getExcludes())); | ||||||
|  |         excludedPaths.addAll(resolveSaIgnorePaths()); | ||||||
|  |         return excludedPaths; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 为路径项添加认证参数 | ||||||
|  |      * | ||||||
|  |      * @param pathItem 当前路径项 | ||||||
|  |      */ | ||||||
|  |     private void addAuthenticationParameters(PathItem pathItem) { | ||||||
|  |         Components components = properties.getComponents(); | ||||||
|  |         if (components == null || MapUtil.isEmpty(components.getSecuritySchemes())) { | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  |         Map<String, SecurityScheme> securitySchemes = components.getSecuritySchemes(); | ||||||
|  |         List<String> schemeNames = securitySchemes.values().stream().map(SecurityScheme::getName).toList(); | ||||||
|  |         pathItem.readOperations().forEach(operation -> { | ||||||
|  |             SecurityRequirement securityRequirement = new SecurityRequirement(); | ||||||
|  |             schemeNames.forEach(securityRequirement::addList); | ||||||
|  |             operation.addSecurityItem(securityRequirement); | ||||||
|  |         }); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 解析所有带有 @SaIgnore 注解的路径 | ||||||
|  |      * | ||||||
|  |      * @return 被忽略的路径集合 | ||||||
|  |      */ | ||||||
|  |     private Set<String> resolveSaIgnorePaths() { | ||||||
|  |         // 获取所有标注 @RestController 的 Bean | ||||||
|  |         Map<String, Object> controllers = context.getBeansWithAnnotation(RestController.class); | ||||||
|  |         Set<String> ignoredPaths = new HashSet<>(); | ||||||
|  |  | ||||||
|  |         // 遍历所有控制器,解析 @SaIgnore 注解路径 | ||||||
|  |         controllers.values().forEach(controllerBean -> { | ||||||
|  |             Class<?> controllerClass = AopUtils.getTargetClass(controllerBean); | ||||||
|  |             List<String> classPaths = getClassPaths(controllerClass); | ||||||
|  |  | ||||||
|  |             // 类级别的 @SaIgnore 注解 | ||||||
|  |             if (controllerClass.isAnnotationPresent(SaIgnore.class)) { | ||||||
|  |                 classPaths.forEach(classPath -> ignoredPaths.add(classPath + "/**")); | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             // 方法级别的 @SaIgnore 注解 | ||||||
|  |             Arrays.stream(controllerClass.getDeclaredMethods()) | ||||||
|  |                 .filter(method -> method.isAnnotationPresent(SaIgnore.class)) | ||||||
|  |                 .forEach(method -> ignoredPaths.addAll(combinePaths(classPaths, getMethodPaths(method)))); | ||||||
|  |         }); | ||||||
|  |  | ||||||
|  |         return ignoredPaths; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 获取类上的所有路径 | ||||||
|  |      * | ||||||
|  |      * @param controller 控制器类 | ||||||
|  |      * @return 类路径列表 | ||||||
|  |      */ | ||||||
|  |     private List<String> getClassPaths(Class<?> controller) { | ||||||
|  |         List<String> classPaths = new ArrayList<>(); | ||||||
|  |         // 处理 @RequestMapping 注解 | ||||||
|  |         if (controller.isAnnotationPresent(RequestMapping.class)) { | ||||||
|  |             RequestMapping mapping = controller.getAnnotation(RequestMapping.class); | ||||||
|  |             classPaths.addAll(Arrays.asList(mapping.value())); | ||||||
|  |         } | ||||||
|  |         // 处理 @CrudRequestMapping 注解 | ||||||
|  |         if (controller.isAnnotationPresent(CrudRequestMapping.class)) { | ||||||
|  |             CrudRequestMapping mapping = controller.getAnnotation(CrudRequestMapping.class); | ||||||
|  |             if (!mapping.value().isEmpty()) { | ||||||
|  |                 classPaths.add(mapping.value()); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         return classPaths; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 获取方法上的所有路径 | ||||||
|  |      * | ||||||
|  |      * @param method 控制器方法 | ||||||
|  |      * @return 方法路径列表 | ||||||
|  |      */ | ||||||
|  |     private List<String> getMethodPaths(Method method) { | ||||||
|  |         List<String> methodPaths = new ArrayList<>(); | ||||||
|  |  | ||||||
|  |         // 检查方法上的各种映射注解 | ||||||
|  |         if (method.isAnnotationPresent(GetMapping.class)) { | ||||||
|  |             methodPaths.addAll(Arrays.asList(method.getAnnotation(GetMapping.class).value())); | ||||||
|  |         } else if (method.isAnnotationPresent(PostMapping.class)) { | ||||||
|  |             methodPaths.addAll(Arrays.asList(method.getAnnotation(PostMapping.class).value())); | ||||||
|  |         } else if (method.isAnnotationPresent(PutMapping.class)) { | ||||||
|  |             methodPaths.addAll(Arrays.asList(method.getAnnotation(PutMapping.class).value())); | ||||||
|  |         } else if (method.isAnnotationPresent(DeleteMapping.class)) { | ||||||
|  |             methodPaths.addAll(Arrays.asList(method.getAnnotation(DeleteMapping.class).value())); | ||||||
|  |         } else if (method.isAnnotationPresent(RequestMapping.class)) { | ||||||
|  |             methodPaths.addAll(Arrays.asList(method.getAnnotation(RequestMapping.class).value())); | ||||||
|  |         } else if (method.isAnnotationPresent(PatchMapping.class)) { | ||||||
|  |             methodPaths.addAll(Arrays.asList(method.getAnnotation(PatchMapping.class).value())); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         return methodPaths; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 组合类路径和方法路径 | ||||||
|  |      * | ||||||
|  |      * @param classPaths  类路径列表 | ||||||
|  |      * @param methodPaths 方法路径列表 | ||||||
|  |      * @return 完整路径集合 | ||||||
|  |      */ | ||||||
|  |     private Set<String> combinePaths(List<String> classPaths, List<String> methodPaths) { | ||||||
|  |         return classPaths.stream() | ||||||
|  |             .flatMap(classPath -> methodPaths.stream().map(methodPath -> classPath + methodPath)) | ||||||
|  |             .collect(Collectors.toSet()); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 检查路径是否在排除列表中 | ||||||
|  |      * | ||||||
|  |      * @param path          当前路径 | ||||||
|  |      * @param excludedPaths 排除路径集合,支持通配符 | ||||||
|  |      * @return 是否匹配排除规则 | ||||||
|  |      */ | ||||||
|  |     private boolean isPathExcluded(String path, Set<String> excludedPaths) { | ||||||
|  |         return excludedPaths.stream().anyMatch(pattern -> pathMatcher.match(pattern, path)); | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -0,0 +1,65 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *     http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | package top.continew.admin.common.config.doc; | ||||||
|  |  | ||||||
|  | import cn.hutool.core.util.StrUtil; | ||||||
|  | import io.swagger.v3.oas.models.Operation; | ||||||
|  | import lombok.RequiredArgsConstructor; | ||||||
|  | import lombok.extern.slf4j.Slf4j; | ||||||
|  | import org.apache.commons.lang.StringUtils; | ||||||
|  | import org.springdoc.core.customizers.GlobalOperationCustomizer; | ||||||
|  | import org.springframework.stereotype.Component; | ||||||
|  | import org.springframework.web.method.HandlerMethod; | ||||||
|  |  | ||||||
|  | import java.util.ArrayList; | ||||||
|  | import java.util.List; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * 全局描述定制器 - 处理 sa-token 的注解权限码 | ||||||
|  |  * | ||||||
|  |  * @author echo | ||||||
|  |  * @since 2025/1/24 14:59 | ||||||
|  |  */ | ||||||
|  | @Slf4j | ||||||
|  | @Component | ||||||
|  | @RequiredArgsConstructor | ||||||
|  | public class GlobalDescriptionCustomizer implements GlobalOperationCustomizer { | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public Operation customize(Operation operation, HandlerMethod handlerMethod) { | ||||||
|  |         // 将 sa-token 注解数据添加到 operation 的描述中 | ||||||
|  |         // 权限 | ||||||
|  |         List<String> noteList = new ArrayList<>(new OperationDescriptionCustomizer().getPermission(handlerMethod)); | ||||||
|  |  | ||||||
|  |         // 如果注解数据列表为空,直接返回原 operation | ||||||
|  |         if (noteList.isEmpty()) { | ||||||
|  |             return operation; | ||||||
|  |         } | ||||||
|  |         // 拼接注解数据为字符串 | ||||||
|  |         String noteStr = StrUtil.join("<br/>", noteList); | ||||||
|  |         // 获取原描述 | ||||||
|  |         String originalDescription = operation.getDescription(); | ||||||
|  |         // 根据原描述是否为空,更新描述 | ||||||
|  |         String newDescription = StringUtils.isNotEmpty(originalDescription) | ||||||
|  |             ? originalDescription + "<br/>" + noteStr | ||||||
|  |             : noteStr; | ||||||
|  |  | ||||||
|  |         // 设置新描述 | ||||||
|  |         operation.setDescription(newDescription); | ||||||
|  |         return operation; | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -0,0 +1,180 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *     http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | package top.continew.admin.common.config.doc; | ||||||
|  |  | ||||||
|  | import cn.dev33.satoken.annotation.SaCheckPermission; | ||||||
|  | import cn.dev33.satoken.annotation.SaCheckRole; | ||||||
|  | import cn.dev33.satoken.annotation.SaMode; | ||||||
|  | import cn.hutool.core.text.CharSequenceUtil; | ||||||
|  | import org.springframework.web.method.HandlerMethod; | ||||||
|  | import top.continew.starter.core.constant.StringConstants; | ||||||
|  | import top.continew.starter.extension.crud.annotation.CrudApi; | ||||||
|  | import top.continew.starter.extension.crud.annotation.CrudRequestMapping; | ||||||
|  | import top.continew.starter.extension.crud.enums.Api; | ||||||
|  |  | ||||||
|  | import java.lang.annotation.Annotation; | ||||||
|  | import java.util.ArrayList; | ||||||
|  | import java.util.List; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Operation 描述定制器 处理 sa-token 鉴权标识符 | ||||||
|  |  * | ||||||
|  |  * @author echo | ||||||
|  |  * @since 2024/6/14 11:18 | ||||||
|  |  */ | ||||||
|  | public class OperationDescriptionCustomizer { | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 获取 sa-token 注解信息 | ||||||
|  |      * | ||||||
|  |      * @param handlerMethod 处理程序方法 | ||||||
|  |      * @return 包含权限和角色校验信息的列表 | ||||||
|  |      */ | ||||||
|  |     public List<String> getPermission(HandlerMethod handlerMethod) { | ||||||
|  |         List<String> values = new ArrayList<>(); | ||||||
|  |  | ||||||
|  |         // 获取权限校验信息 | ||||||
|  |         String permissionInfo = getAnnotationInfo(handlerMethod, SaCheckPermission.class, "权限校验:"); | ||||||
|  |         if (!permissionInfo.isEmpty()) { | ||||||
|  |             values.add(permissionInfo); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         // 获取角色校验信息 | ||||||
|  |         String roleInfo = getAnnotationInfo(handlerMethod, SaCheckRole.class, "角色校验:"); | ||||||
|  |         if (!roleInfo.isEmpty()) { | ||||||
|  |             values.add(roleInfo); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         // 处理 CrudRequestMapping 和 CrudApi 注解生成的权限信息 | ||||||
|  |         String crudPermissionInfo = getCrudPermissionInfo(handlerMethod); | ||||||
|  |         if (!crudPermissionInfo.isEmpty()) { | ||||||
|  |             values.add(crudPermissionInfo); | ||||||
|  |         } | ||||||
|  |         return values; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 获取类和方法上指定注解的信息 | ||||||
|  |      * | ||||||
|  |      * @param handlerMethod   处理程序方法 | ||||||
|  |      * @param annotationClass 注解类 | ||||||
|  |      * @param title           信息标题 | ||||||
|  |      * @param <A>             注解类型 | ||||||
|  |      * @return 拼接好的注解信息字符串 | ||||||
|  |      */ | ||||||
|  |     @SuppressWarnings("unchecked") | ||||||
|  |     private <A extends Annotation> String getAnnotationInfo(HandlerMethod handlerMethod, | ||||||
|  |                                                             Class<A> annotationClass, | ||||||
|  |                                                             String title) { | ||||||
|  |         StringBuilder infoBuilder = new StringBuilder(); | ||||||
|  |  | ||||||
|  |         // 获取类上的注解 | ||||||
|  |         A classAnnotation = handlerMethod.getBeanType().getAnnotation(annotationClass); | ||||||
|  |         if (classAnnotation != null) { | ||||||
|  |             appendAnnotationInfo(infoBuilder, "类:", classAnnotation); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         // 获取方法上的注解 | ||||||
|  |         A methodAnnotation = handlerMethod.getMethodAnnotation(annotationClass); | ||||||
|  |         if (methodAnnotation != null) { | ||||||
|  |             appendAnnotationInfo(infoBuilder, "方法:", methodAnnotation); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         // 如果有注解信息,添加标题 | ||||||
|  |         if (!infoBuilder.isEmpty()) { | ||||||
|  |             infoBuilder.insert(0, "<font style=\"color:red\" class=\"light-red\">" + title + "</font></br>"); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         return infoBuilder.toString(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 拼接注解信息到 StringBuilder 中 | ||||||
|  |      * | ||||||
|  |      * @param builder    用于拼接信息的 StringBuilder | ||||||
|  |      * @param prefix     前缀信息,如 "类:" 或 "方法:" | ||||||
|  |      * @param annotation 注解对象 | ||||||
|  |      */ | ||||||
|  |     private void appendAnnotationInfo(StringBuilder builder, String prefix, Annotation annotation) { | ||||||
|  |         String[] values = null; | ||||||
|  |         SaMode mode = null; | ||||||
|  |         String type = ""; | ||||||
|  |         String[] orRole = new String[0]; | ||||||
|  |  | ||||||
|  |         if (annotation instanceof SaCheckPermission checkPermission) { | ||||||
|  |             values = checkPermission.value(); | ||||||
|  |             mode = checkPermission.mode(); | ||||||
|  |             type = checkPermission.type(); | ||||||
|  |             orRole = checkPermission.orRole(); | ||||||
|  |         } else if (annotation instanceof SaCheckRole checkRole) { | ||||||
|  |             values = checkRole.value(); | ||||||
|  |             mode = checkRole.mode(); | ||||||
|  |             type = checkRole.type(); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if (values != null && mode != null) { | ||||||
|  |             builder.append("<font style=\"color:red\" class=\"light-red\">"); | ||||||
|  |             builder.append(prefix); | ||||||
|  |             if (!type.isEmpty()) { | ||||||
|  |                 builder.append("(类型:").append(type).append(")"); | ||||||
|  |             } | ||||||
|  |             builder.append(getAnnotationNote(values, mode)); | ||||||
|  |             if (orRole.length > 0) { | ||||||
|  |                 builder.append(" 或 角色校验(").append(getAnnotationNote(orRole, mode)).append(")"); | ||||||
|  |             } | ||||||
|  |             builder.append("</font></br>"); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 根据注解的模式拼接注解值 | ||||||
|  |      * | ||||||
|  |      * @param values 注解的值数组 | ||||||
|  |      * @param mode   注解的模式(AND 或 OR) | ||||||
|  |      * @return 拼接好的注解值字符串 | ||||||
|  |      */ | ||||||
|  |     private String getAnnotationNote(String[] values, SaMode mode) { | ||||||
|  |         if (mode.equals(SaMode.AND)) { | ||||||
|  |             return String.join(" 且 ", values); | ||||||
|  |         } else { | ||||||
|  |             return String.join(" 或 ", values); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 处理 CrudRequestMapping 和 CrudApi 注解生成的权限信息 | ||||||
|  |      * | ||||||
|  |      * @param handlerMethod 处理程序方法 | ||||||
|  |      * @return 拼接好的权限信息字符串 | ||||||
|  |      */ | ||||||
|  |     private String getCrudPermissionInfo(HandlerMethod handlerMethod) { | ||||||
|  |         CrudRequestMapping crudRequestMapping = handlerMethod.getBeanType().getAnnotation(CrudRequestMapping.class); | ||||||
|  |         CrudApi crudApi = handlerMethod.getMethodAnnotation(CrudApi.class); | ||||||
|  |  | ||||||
|  |         if (crudRequestMapping == null || crudApi == null) { | ||||||
|  |             return ""; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         String path = crudRequestMapping.value(); | ||||||
|  |         String prefix = String.join(StringConstants.COLON, CharSequenceUtil.splitTrim(path, StringConstants.SLASH)); | ||||||
|  |         Api api = crudApi.value(); | ||||||
|  |         String apiName = Api.PAGE.equals(api) || Api.TREE.equals(api) ? Api.LIST.name() : api.name(); | ||||||
|  |         String permission = "%s:%s".formatted(prefix, apiName.toLowerCase()); | ||||||
|  |  | ||||||
|  |         return "<font style=\"color:red\" class=\"light-red\">Crud 权限校验:</font></br><font style=\"color:red\" class=\"light-red\">方法:</font><font style=\"color:red\" class=\"light-red\">" + permission + "</font>"; | ||||||
|  |     } | ||||||
|  | } | ||||||
		Reference in New Issue
	
	Block a user