mirror of
https://gitee.com/dromara/RuoYi-Vue-Plus.git
synced 2026-04-10 04:23:16 +08:00
update 通过类加载的方式优化 Javadoc 解析器,支持执行多个 Javadoc 解析器
This commit is contained in:
@@ -21,11 +21,6 @@
|
|||||||
<artifactId>ruoyi-common-core</artifactId>
|
<artifactId>ruoyi-common-core</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
|
||||||
<groupId>cn.dev33</groupId>
|
|
||||||
<artifactId>sa-token-core</artifactId>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springdoc</groupId>
|
<groupId>org.springdoc</groupId>
|
||||||
<artifactId>springdoc-openapi-starter-webmvc-api</artifactId>
|
<artifactId>springdoc-openapi-starter-webmvc-api</artifactId>
|
||||||
|
|||||||
@@ -7,8 +7,8 @@ import io.swagger.v3.oas.models.security.SecurityRequirement;
|
|||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import org.dromara.common.core.utils.StringUtils;
|
import org.dromara.common.core.utils.StringUtils;
|
||||||
import org.dromara.common.doc.config.properties.SpringDocProperties;
|
import org.dromara.common.doc.config.properties.SpringDocProperties;
|
||||||
import org.dromara.common.doc.core.enhancer.SaTokenJavadocResolver;
|
import org.dromara.common.doc.core.resolver.JavadocResolver;
|
||||||
import org.dromara.common.doc.core.enhancer.SaTokenMetadataResolver;
|
import org.dromara.common.doc.core.resolver.SaTokenAnnotationMetadataJavadocResolver;
|
||||||
import org.dromara.common.doc.handler.OpenApiHandler;
|
import org.dromara.common.doc.handler.OpenApiHandler;
|
||||||
import org.springdoc.core.configuration.SpringDocConfiguration;
|
import org.springdoc.core.configuration.SpringDocConfiguration;
|
||||||
import org.springdoc.core.customizers.OpenApiBuilderCustomizer;
|
import org.springdoc.core.customizers.OpenApiBuilderCustomizer;
|
||||||
@@ -87,8 +87,8 @@ public class SpringDocConfig {
|
|||||||
SpringDocConfigProperties springDocConfigProperties, PropertyResolverUtils propertyResolverUtils,
|
SpringDocConfigProperties springDocConfigProperties, PropertyResolverUtils propertyResolverUtils,
|
||||||
Optional<List<OpenApiBuilderCustomizer>> openApiBuilderCustomisers,
|
Optional<List<OpenApiBuilderCustomizer>> openApiBuilderCustomisers,
|
||||||
Optional<List<ServerBaseUrlCustomizer>> serverBaseUrlCustomisers, Optional<JavadocProvider> javadocProvider,
|
Optional<List<ServerBaseUrlCustomizer>> serverBaseUrlCustomisers, Optional<JavadocProvider> javadocProvider,
|
||||||
SaTokenMetadataResolver saTokenMetadataResolver) {
|
List<JavadocResolver> javadocResolvers) {
|
||||||
return new OpenApiHandler(openAPI, securityParser, springDocConfigProperties, propertyResolverUtils, openApiBuilderCustomisers, serverBaseUrlCustomisers, javadocProvider, saTokenMetadataResolver);
|
return new OpenApiHandler(openAPI, securityParser, springDocConfigProperties, propertyResolverUtils, openApiBuilderCustomisers, serverBaseUrlCustomisers, javadocProvider, javadocResolvers);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -116,11 +116,11 @@ public class SpringDocConfig {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 注册JavaDoc权限解析器
|
* 注册SaToken JavaDoc权限注解解析器
|
||||||
*/
|
*/
|
||||||
@Bean
|
@Bean
|
||||||
public SaTokenMetadataResolver saTokenJavadocResolver() {
|
public JavadocResolver saTokenAnnotationJavadocResolver() {
|
||||||
return new SaTokenJavadocResolver();
|
return new SaTokenAnnotationMetadataJavadocResolver();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1,185 +0,0 @@
|
|||||||
package org.dromara.common.doc.core.enhancer;
|
|
||||||
|
|
||||||
import cn.dev33.satoken.annotation.SaCheckLogin;
|
|
||||||
import cn.dev33.satoken.annotation.SaCheckPermission;
|
|
||||||
import cn.dev33.satoken.annotation.SaCheckRole;
|
|
||||||
import cn.dev33.satoken.annotation.SaIgnore;
|
|
||||||
import cn.hutool.core.convert.Convert;
|
|
||||||
import io.swagger.v3.oas.models.Operation;
|
|
||||||
import org.dromara.common.doc.core.model.SaTokenSecurityMetadata;
|
|
||||||
import org.springframework.web.method.HandlerMethod;
|
|
||||||
|
|
||||||
import java.lang.annotation.Annotation;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 基于JavaDoc的SaToken权限解析器
|
|
||||||
*
|
|
||||||
* @author echo
|
|
||||||
*/
|
|
||||||
public class SaTokenJavadocResolver implements SaTokenMetadataResolver {
|
|
||||||
|
|
||||||
public static final Class<SaCheckRole> SA_CHECK_ROLE_CLASS = SaCheckRole.class;
|
|
||||||
public static final Class<SaCheckPermission> SA_CHECK_PERMISSION_CLASS = SaCheckPermission.class;
|
|
||||||
public static final Class<SaIgnore> SA_IGNORE_CLASS = SaIgnore.class;
|
|
||||||
public static final Class<SaCheckLogin> SA_CHECK_LOGIN = SaCheckLogin.class;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 核心解析方法
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void resolve(HandlerMethod handlerMethod, Operation operation, SaTokenSecurityMetadata metadata) {
|
|
||||||
// 检查是否忽略校验
|
|
||||||
if (isIgnore(handlerMethod)) {
|
|
||||||
metadata.setIgnore(true);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 解析权限校验
|
|
||||||
resolvePermissionCheck(handlerMethod, metadata);
|
|
||||||
|
|
||||||
// 解析角色校验
|
|
||||||
resolveRoleCheck(handlerMethod, metadata);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 解析器优先级
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public int getOrder() {
|
|
||||||
return 100;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 判断是否支持当前HandlerMethod
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public boolean supports(HandlerMethod handlerMethod) {
|
|
||||||
return hasAnnotation(handlerMethod
|
|
||||||
.getMethodAnnotation(SA_CHECK_PERMISSION_CLASS)) || hasAnnotation(handlerMethod
|
|
||||||
.getMethodAnnotation(SA_CHECK_ROLE_CLASS)) || hasAnnotation(handlerMethod
|
|
||||||
.getMethodAnnotation(SA_IGNORE_CLASS)) || hasAnnotation(handlerMethod
|
|
||||||
.getBeanType()
|
|
||||||
.getAnnotation(SA_CHECK_PERMISSION_CLASS)) || hasAnnotation(handlerMethod
|
|
||||||
.getBeanType()
|
|
||||||
.getAnnotation(SA_CHECK_ROLE_CLASS)) || hasAnnotation(handlerMethod
|
|
||||||
.getBeanType()
|
|
||||||
.getAnnotation(SA_IGNORE_CLASS));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getName() {
|
|
||||||
return "SaTokenJavadocResolver";
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 检查是否忽略校验
|
|
||||||
*/
|
|
||||||
private boolean isIgnore(HandlerMethod handlerMethod) {
|
|
||||||
// 检查方法上的注解
|
|
||||||
if (hasAnnotation(handlerMethod.getMethodAnnotation(SA_IGNORE_CLASS))) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
// 检查类上的注解
|
|
||||||
return hasAnnotation(handlerMethod.getBeanType().getAnnotation(SA_IGNORE_CLASS));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 解析权限校验
|
|
||||||
*/
|
|
||||||
private void resolvePermissionCheck(HandlerMethod handlerMethod, SaTokenSecurityMetadata metadata) {
|
|
||||||
// 获取方法上的注解
|
|
||||||
Annotation methodAnnotation = handlerMethod
|
|
||||||
.getMethodAnnotation(SA_CHECK_PERMISSION_CLASS);
|
|
||||||
// 获取类上的注解
|
|
||||||
Annotation classAnnotation = handlerMethod.getBeanType()
|
|
||||||
.getAnnotation(SA_CHECK_PERMISSION_CLASS);
|
|
||||||
|
|
||||||
// 解析权限信息
|
|
||||||
if (hasAnnotation(methodAnnotation)) {
|
|
||||||
resolvePermissionAnnotation(metadata, methodAnnotation);
|
|
||||||
}
|
|
||||||
if (hasAnnotation(classAnnotation)) {
|
|
||||||
resolvePermissionAnnotation(metadata, classAnnotation);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 解析权限注解
|
|
||||||
*/
|
|
||||||
private void resolvePermissionAnnotation(SaTokenSecurityMetadata metadata, Annotation annotation) {
|
|
||||||
try {
|
|
||||||
// 反射获取注解属性
|
|
||||||
Object value = getAnnotationValue(annotation, "value");
|
|
||||||
Object mode = getAnnotationValue(annotation, "mode");
|
|
||||||
Object type = getAnnotationValue(annotation, "type");
|
|
||||||
Object orRole = getAnnotationValue(annotation, "orRole");
|
|
||||||
|
|
||||||
String[] values = Convert.toStrArray(value);
|
|
||||||
String modeStr = mode != null ? mode.toString() : "AND";
|
|
||||||
String typeStr = type != null ? type.toString() : "";
|
|
||||||
String[] orRoles = Convert.toStrArray(orRole);
|
|
||||||
|
|
||||||
metadata.addPermission(values, modeStr, typeStr, orRoles);
|
|
||||||
} catch (Exception ignore) {
|
|
||||||
// 忽略解析错误
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 解析角色校验
|
|
||||||
*/
|
|
||||||
private void resolveRoleCheck(HandlerMethod handlerMethod, SaTokenSecurityMetadata metadata) {
|
|
||||||
// 获取方法上的注解
|
|
||||||
Annotation methodAnnotation = handlerMethod.getMethodAnnotation(SA_CHECK_ROLE_CLASS);
|
|
||||||
// 获取类上的注解
|
|
||||||
Annotation classAnnotation = handlerMethod.getBeanType()
|
|
||||||
.getAnnotation(SA_CHECK_ROLE_CLASS);
|
|
||||||
|
|
||||||
// 解析角色信息
|
|
||||||
if (hasAnnotation(methodAnnotation)) {
|
|
||||||
resolveRoleAnnotation(metadata, methodAnnotation);
|
|
||||||
}
|
|
||||||
if (hasAnnotation(classAnnotation)) {
|
|
||||||
resolveRoleAnnotation(metadata, classAnnotation);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 解析角色注解
|
|
||||||
*/
|
|
||||||
private void resolveRoleAnnotation(SaTokenSecurityMetadata metadata, Annotation annotation) {
|
|
||||||
try {
|
|
||||||
// 反射获取注解属性
|
|
||||||
Object value = getAnnotationValue(annotation, "value");
|
|
||||||
Object mode = getAnnotationValue(annotation, "mode");
|
|
||||||
Object type = getAnnotationValue(annotation, "type");
|
|
||||||
|
|
||||||
String[] values = Convert.toStrArray(value);
|
|
||||||
String modeStr = mode != null ? mode.toString() : "AND";
|
|
||||||
String typeStr = type != null ? type.toString() : "";
|
|
||||||
|
|
||||||
metadata.addRole(values, modeStr, typeStr);
|
|
||||||
} catch (Exception ignore) {
|
|
||||||
// 忽略解析错误
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 检查注解是否存在
|
|
||||||
*/
|
|
||||||
private boolean hasAnnotation(Annotation annotation) {
|
|
||||||
return annotation != null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取注解属性值
|
|
||||||
*/
|
|
||||||
private Object getAnnotationValue(Annotation annotation, String attributeName) {
|
|
||||||
try {
|
|
||||||
return annotation.annotationType().getMethod(attributeName).invoke(annotation);
|
|
||||||
} catch (Exception e) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,38 +0,0 @@
|
|||||||
package org.dromara.common.doc.core.enhancer;
|
|
||||||
|
|
||||||
import io.swagger.v3.oas.models.Operation;
|
|
||||||
import org.dromara.common.doc.core.model.SaTokenSecurityMetadata;
|
|
||||||
import org.springframework.web.method.HandlerMethod;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 权限元数据解析器接口
|
|
||||||
*
|
|
||||||
* @author echo
|
|
||||||
*/
|
|
||||||
public interface SaTokenMetadataResolver {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 解析权限元数据
|
|
||||||
*/
|
|
||||||
void resolve(HandlerMethod handlerMethod, Operation operation, SaTokenSecurityMetadata metadata);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取解析器优先级
|
|
||||||
*/
|
|
||||||
int getOrder();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 判断是否支持当前HandlerMethod
|
|
||||||
*/
|
|
||||||
boolean supports(HandlerMethod handlerMethod);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取解析器的名称
|
|
||||||
*
|
|
||||||
* @return 解析器名称
|
|
||||||
*/
|
|
||||||
default String getName() {
|
|
||||||
return this.getClass().getSimpleName();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,163 @@
|
|||||||
|
package org.dromara.common.doc.core.resolver;
|
||||||
|
|
||||||
|
import cn.hutool.core.annotation.AnnotationUtil;
|
||||||
|
import cn.hutool.core.util.ClassLoaderUtil;
|
||||||
|
import io.swagger.v3.oas.models.Operation;
|
||||||
|
import org.springframework.web.method.HandlerMethod;
|
||||||
|
|
||||||
|
import java.lang.annotation.Annotation;
|
||||||
|
import java.lang.reflect.AnnotatedElement;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 抽象元数据 Javadoc 解析器
|
||||||
|
*
|
||||||
|
* @param <M> 元数据类型
|
||||||
|
* @author 秋辞未寒
|
||||||
|
*/
|
||||||
|
public abstract class AbstractMetadataJavadocResolver<M> implements JavadocResolver {
|
||||||
|
|
||||||
|
public static final int HIGHEST_PRECEDENCE = Integer.MIN_VALUE;
|
||||||
|
public static final int LOWEST_PRECEDENCE = Integer.MAX_VALUE;
|
||||||
|
|
||||||
|
private final Supplier<M> metadataProvider;
|
||||||
|
|
||||||
|
private final int order;
|
||||||
|
|
||||||
|
public AbstractMetadataJavadocResolver(Supplier<M> metadataProvider) {
|
||||||
|
this(metadataProvider, LOWEST_PRECEDENCE);
|
||||||
|
}
|
||||||
|
|
||||||
|
public AbstractMetadataJavadocResolver(Supplier<M> metadataProvider, int order) {
|
||||||
|
this.metadataProvider = metadataProvider;
|
||||||
|
this.order = order;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getOrder() {
|
||||||
|
return order;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String resolve(HandlerMethod handlerMethod, Operation operation) {
|
||||||
|
return resolve(handlerMethod, operation, metadataProvider.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 执行解析并返回解析到的 Javadoc 内容
|
||||||
|
* @param handlerMethod 处理器方法
|
||||||
|
* @param operation Swagger Operation实例
|
||||||
|
* @param metadata 元信息
|
||||||
|
* @return 解析到的 Javadoc 内容
|
||||||
|
*/
|
||||||
|
public abstract String resolve(HandlerMethod handlerMethod, Operation operation, M metadata);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查处理器方法所属的类上是否存在注解
|
||||||
|
* @param handlerMethod 处理器方法
|
||||||
|
* @param annotationClass 注解类
|
||||||
|
* @return 是否存在注解
|
||||||
|
*/
|
||||||
|
public boolean hasClassAnnotation(HandlerMethod handlerMethod,Class<? extends Annotation> annotationClass){
|
||||||
|
return AnnotationUtil.hasAnnotation(handlerMethod.getBeanType(), annotationClass);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查处理器方法所属的类上是否存在注解
|
||||||
|
* @param handlerMethod 处理器方法
|
||||||
|
* @param annotationTypeName 注解类名称
|
||||||
|
* @return 是否存在注解
|
||||||
|
*/
|
||||||
|
public boolean hasClassAnnotation(HandlerMethod handlerMethod, String annotationTypeName){
|
||||||
|
return AnnotationUtil.hasAnnotation(handlerMethod.getBeanType(), annotationTypeName);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查处理器方法上是否存在注解
|
||||||
|
* @param handlerMethod 处理器方法
|
||||||
|
* @param annotationClass 注解类
|
||||||
|
* @return 是否存在注解
|
||||||
|
*/
|
||||||
|
public boolean hasMethodAnnotation(HandlerMethod handlerMethod,Class<? extends Annotation> annotationClass){
|
||||||
|
return AnnotationUtil.hasAnnotation(handlerMethod.getMethod(), annotationClass);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查处理器方法上是否存在注解
|
||||||
|
* @param handlerMethod 处理器方法
|
||||||
|
* @param annotationTypeName 注解类名称
|
||||||
|
* @return 是否存在注解
|
||||||
|
*/
|
||||||
|
public boolean hasMethodAnnotation(HandlerMethod handlerMethod, String annotationTypeName){
|
||||||
|
return AnnotationUtil.hasAnnotation(handlerMethod.getMethod(), annotationTypeName);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查处理器方法上是否存在注解
|
||||||
|
* @param handlerMethod 处理器方法
|
||||||
|
* @param annotationClass 注解类
|
||||||
|
* @return 是否存在注解
|
||||||
|
*/
|
||||||
|
public boolean hasAnnotation(HandlerMethod handlerMethod,Class<? extends Annotation> annotationClass){
|
||||||
|
return this.hasClassAnnotation(handlerMethod, annotationClass) || this.hasMethodAnnotation(handlerMethod, annotationClass);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查处理器方法上是否存在注解
|
||||||
|
* @param handlerMethod 处理器方法
|
||||||
|
* @param annotationTypeName 注解类名称
|
||||||
|
* @return 是否存在注解
|
||||||
|
*/
|
||||||
|
public boolean hasAnnotation(HandlerMethod handlerMethod, String annotationTypeName){
|
||||||
|
return this.hasClassAnnotation(handlerMethod, annotationTypeName) || this.hasMethodAnnotation(handlerMethod, annotationTypeName);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取处理器方法所属类上的注解的值
|
||||||
|
* @param handlerMethod 处理器方法
|
||||||
|
* @param annotationClass 注解类
|
||||||
|
* @return 注解的值
|
||||||
|
*/
|
||||||
|
public Map<String, Object> getClassAnnotationValueMap(HandlerMethod handlerMethod, Class<? extends Annotation> annotationClass) {
|
||||||
|
return AnnotationUtil.getAnnotationValueMap(handlerMethod.getBeanType(), annotationClass);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取处理器方法所属类上的注解的值
|
||||||
|
* @param handlerMethod 处理器方法
|
||||||
|
* @param annotationClassName 注解类名称
|
||||||
|
* @return 注解的值
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public Map<String, Object> getClassAnnotationValueMap(HandlerMethod handlerMethod, String annotationClassName) {
|
||||||
|
Class<? extends Annotation> annotationClass = (Class<? extends Annotation>) ClassLoaderUtil.loadClass(annotationClassName, false);
|
||||||
|
return AnnotationUtil.getAnnotationValueMap(handlerMethod.getBeanType(), annotationClass);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取处理器方法上的注解的值
|
||||||
|
* @param handlerMethod 处理器方法
|
||||||
|
* @param annotationClass 注解类
|
||||||
|
* @return 注解的值
|
||||||
|
*/
|
||||||
|
public Map<String, Object> getMethodAnnotationValueMap(HandlerMethod handlerMethod, Class<? extends Annotation> annotationClass) {
|
||||||
|
return AnnotationUtil.getAnnotationValueMap(handlerMethod.getMethod(), annotationClass);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取处理器方法所属类上的注解的值
|
||||||
|
* @param handlerMethod 处理器方法
|
||||||
|
* @param annotationClassName 注解类名称
|
||||||
|
* @return 注解的值
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public Map<String, Object> getMethodAnnotationValueMap(HandlerMethod handlerMethod, String annotationClassName) {
|
||||||
|
Class<? extends Annotation> annotationClass = (Class<? extends Annotation>) ClassLoaderUtil.loadClass(annotationClassName, false);
|
||||||
|
return AnnotationUtil.getAnnotationValueMap(handlerMethod.getMethod(), annotationClass);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Map<String, Object> getAnnotationValueMap(AnnotatedElement annotatedElement, Class<? extends Annotation> annotationClass) {
|
||||||
|
return AnnotationUtil.getAnnotationValueMap(annotatedElement, annotationClass);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
package org.dromara.common.doc.core.resolver;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.models.Operation;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.springframework.core.Ordered;
|
||||||
|
import org.springframework.web.method.HandlerMethod;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Javadoc解析器接口
|
||||||
|
*
|
||||||
|
* @author echo
|
||||||
|
* @author 秋辞未寒
|
||||||
|
*/
|
||||||
|
public interface JavadocResolver extends Comparable<JavadocResolver>, Ordered {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查解析器是否支持解析 HandlerMethod
|
||||||
|
* @param handlerMethod 处理器方法
|
||||||
|
* @return 是否支持解析
|
||||||
|
*/
|
||||||
|
boolean supports(HandlerMethod handlerMethod);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 执行解析并返回解析到的 Javadoc 内容
|
||||||
|
* @param handlerMethod 处理器方法
|
||||||
|
* @param operation Swagger Operation实例
|
||||||
|
* @return 解析到的 Javadoc 内容
|
||||||
|
*/
|
||||||
|
String resolve(HandlerMethod handlerMethod, Operation operation);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取解析器优先级
|
||||||
|
*/
|
||||||
|
default int getOrder() {
|
||||||
|
return Ordered.LOWEST_PRECEDENCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取解析器的名称
|
||||||
|
*
|
||||||
|
* @return 解析器名称
|
||||||
|
*/
|
||||||
|
default String getName() {
|
||||||
|
return this.getClass().getSimpleName();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
default int compareTo(@NotNull JavadocResolver o) {
|
||||||
|
return Integer.compare(getOrder(), o.getOrder());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,163 @@
|
|||||||
|
package org.dromara.common.doc.core.resolver;
|
||||||
|
|
||||||
|
import cn.hutool.core.convert.Convert;
|
||||||
|
import cn.hutool.core.util.ClassLoaderUtil;
|
||||||
|
import io.swagger.v3.oas.models.Operation;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.dromara.common.doc.core.model.SaTokenSecurityMetadata;
|
||||||
|
import org.springframework.web.method.HandlerMethod;
|
||||||
|
|
||||||
|
import java.lang.annotation.Annotation;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 基于JavaDoc的SaToken权限解析器
|
||||||
|
*
|
||||||
|
* @author echo
|
||||||
|
* @author 秋辞未寒
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
@Slf4j
|
||||||
|
public class SaTokenAnnotationMetadataJavadocResolver extends AbstractMetadataJavadocResolver<SaTokenSecurityMetadata> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 默认元数据提供者,每次解析都会创建一个新的元数据对象
|
||||||
|
*/
|
||||||
|
public static final Supplier<SaTokenSecurityMetadata> DEFAULT_METADATA_PROVIDER = SaTokenSecurityMetadata::new;
|
||||||
|
|
||||||
|
private static final String SA_CHECK_ROLE_CLASS_NAME = "cn.dev33.satoken.annotation.SaCheckRole";
|
||||||
|
private static final String SA_CHECK_PERMISSION_CLASS_NAME = "cn.dev33.satoken.annotation.SaCheckPermission";
|
||||||
|
private static final String SA_IGNORE_CLASS_NAME = "cn.dev33.satoken.annotation.SaIgnore";
|
||||||
|
private static final String SA_CHECK_LOGIN_NAME = "cn.dev33.satoken.annotation.SaCheckLogin";
|
||||||
|
|
||||||
|
private static final Class<? extends Annotation> SA_CHECK_ROLE_CLASS;
|
||||||
|
private static final Class<? extends Annotation> SA_CHECK_PERMISSION_CLASS;
|
||||||
|
private static final Class<? extends Annotation> SA_IGNORE_CLASS;
|
||||||
|
private static final Class<? extends Annotation> SA_CHECK_LOGIN_CLASS;
|
||||||
|
|
||||||
|
|
||||||
|
static {
|
||||||
|
// 通过类加载器去加载注解类Class实例
|
||||||
|
SA_CHECK_ROLE_CLASS = (Class<? extends Annotation>) ClassLoaderUtil.loadClass(SA_CHECK_ROLE_CLASS_NAME, false);
|
||||||
|
SA_CHECK_PERMISSION_CLASS = (Class<? extends Annotation>) ClassLoaderUtil.loadClass(SA_CHECK_PERMISSION_CLASS_NAME, false);
|
||||||
|
SA_IGNORE_CLASS = (Class<? extends Annotation>) ClassLoaderUtil.loadClass(SA_IGNORE_CLASS_NAME, false);
|
||||||
|
SA_CHECK_LOGIN_CLASS = (Class<? extends Annotation>) ClassLoaderUtil.loadClass(SA_CHECK_LOGIN_NAME, false);
|
||||||
|
if (log.isInfoEnabled()) {
|
||||||
|
log.info("SaTokenAnnotationJavadocResolver init success, load annotation class: {}", List.of(SA_CHECK_ROLE_CLASS, SA_CHECK_PERMISSION_CLASS, SA_IGNORE_CLASS, SA_CHECK_LOGIN_CLASS));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public SaTokenAnnotationMetadataJavadocResolver() {
|
||||||
|
this(DEFAULT_METADATA_PROVIDER);
|
||||||
|
}
|
||||||
|
|
||||||
|
public SaTokenAnnotationMetadataJavadocResolver(Supplier<SaTokenSecurityMetadata> metadataProvider) {
|
||||||
|
super(metadataProvider);
|
||||||
|
}
|
||||||
|
|
||||||
|
public SaTokenAnnotationMetadataJavadocResolver(int order) {
|
||||||
|
this(DEFAULT_METADATA_PROVIDER,order);
|
||||||
|
}
|
||||||
|
|
||||||
|
public SaTokenAnnotationMetadataJavadocResolver(Supplier<SaTokenSecurityMetadata> metadataProvider, int order) {
|
||||||
|
super(metadataProvider,order);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supports(HandlerMethod handlerMethod) {
|
||||||
|
return hasAnnotation(handlerMethod, SA_CHECK_ROLE_CLASS) || hasAnnotation(handlerMethod, SA_CHECK_PERMISSION_CLASS) || hasAnnotation(handlerMethod, SA_IGNORE_CLASS);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String resolve(HandlerMethod handlerMethod, Operation operation, SaTokenSecurityMetadata metadata) {
|
||||||
|
// 检查是否忽略校验
|
||||||
|
if(hasAnnotation(handlerMethod, SA_IGNORE_CLASS_NAME)){
|
||||||
|
metadata.setIgnore(true);
|
||||||
|
return metadata.toMarkdownString();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 解析权限校验
|
||||||
|
resolvePermissionCheck(handlerMethod, metadata);
|
||||||
|
|
||||||
|
// 解析角色校验
|
||||||
|
resolveRoleCheck(handlerMethod, metadata);
|
||||||
|
return metadata.toMarkdownString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析权限校验
|
||||||
|
*/
|
||||||
|
private void resolvePermissionCheck(HandlerMethod handlerMethod, SaTokenSecurityMetadata metadata) {
|
||||||
|
// 解析获取方法上的注解角色信息
|
||||||
|
if (hasMethodAnnotation(handlerMethod, SA_CHECK_PERMISSION_CLASS_NAME)) {
|
||||||
|
Map<String, Object> annotationValueMap = getMethodAnnotationValueMap(handlerMethod, SA_CHECK_PERMISSION_CLASS);
|
||||||
|
resolvePermissionAnnotation(metadata, annotationValueMap);
|
||||||
|
}
|
||||||
|
// 解析获取类上的注解角色信息
|
||||||
|
if (hasClassAnnotation(handlerMethod, SA_CHECK_PERMISSION_CLASS_NAME)) {
|
||||||
|
Map<String, Object> annotationValueMap = getClassAnnotationValueMap(handlerMethod, SA_CHECK_PERMISSION_CLASS);
|
||||||
|
resolvePermissionAnnotation(metadata, annotationValueMap);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析权限注解
|
||||||
|
*/
|
||||||
|
private void resolvePermissionAnnotation(SaTokenSecurityMetadata metadata, Map<String, Object> annotationValueMap) {
|
||||||
|
try {
|
||||||
|
// 反射获取注解属性
|
||||||
|
Object value = annotationValueMap.get( "value");
|
||||||
|
Object mode = annotationValueMap.get( "mode");
|
||||||
|
Object type = annotationValueMap.get( "type");
|
||||||
|
Object orRole = annotationValueMap.get( "orRole");
|
||||||
|
|
||||||
|
String[] values = Convert.toStrArray(value);
|
||||||
|
String modeStr = mode != null ? mode.toString() : "AND";
|
||||||
|
String typeStr = type != null ? type.toString() : "";
|
||||||
|
String[] orRoles = Convert.toStrArray(orRole);
|
||||||
|
|
||||||
|
metadata.addPermission(values, modeStr, typeStr, orRoles);
|
||||||
|
} catch (Exception ignore) {
|
||||||
|
// 忽略解析错误
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析角色校验
|
||||||
|
*/
|
||||||
|
private void resolveRoleCheck(HandlerMethod handlerMethod, SaTokenSecurityMetadata metadata) {
|
||||||
|
// 解析获取方法上的注解角色信息
|
||||||
|
if (hasMethodAnnotation(handlerMethod, SA_CHECK_ROLE_CLASS_NAME)) {
|
||||||
|
Map<String, Object> annotationValueMap = getMethodAnnotationValueMap(handlerMethod, SA_CHECK_ROLE_CLASS);
|
||||||
|
resolveRoleAnnotation(metadata, annotationValueMap);
|
||||||
|
}
|
||||||
|
// 解析获取类上的注解角色信息
|
||||||
|
if (hasClassAnnotation(handlerMethod, SA_CHECK_ROLE_CLASS_NAME)) {
|
||||||
|
Map<String, Object> annotationValueMap = getClassAnnotationValueMap(handlerMethod, SA_CHECK_ROLE_CLASS);
|
||||||
|
resolveRoleAnnotation(metadata, annotationValueMap);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析角色注解
|
||||||
|
*/
|
||||||
|
private void resolveRoleAnnotation(SaTokenSecurityMetadata metadata, Map<String, Object> annotationValueMap) {
|
||||||
|
try {
|
||||||
|
// 反射获取注解属性
|
||||||
|
Object value = annotationValueMap.get("value");
|
||||||
|
Object mode = annotationValueMap.get("mode");
|
||||||
|
Object type = annotationValueMap.get("type");
|
||||||
|
|
||||||
|
String[] values = Convert.toStrArray(value);
|
||||||
|
String modeStr = mode != null ? mode.toString() : "AND";
|
||||||
|
String typeStr = type != null ? type.toString() : "";
|
||||||
|
|
||||||
|
metadata.addRole(values, modeStr, typeStr);
|
||||||
|
} catch (Exception ignore) {
|
||||||
|
// 忽略解析错误
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -12,8 +12,7 @@ import io.swagger.v3.oas.models.tags.Tag;
|
|||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.dromara.common.core.utils.StreamUtils;
|
import org.dromara.common.core.utils.StreamUtils;
|
||||||
import org.dromara.common.doc.core.enhancer.SaTokenMetadataResolver;
|
import org.dromara.common.doc.core.resolver.JavadocResolver;
|
||||||
import org.dromara.common.doc.core.model.SaTokenSecurityMetadata;
|
|
||||||
import org.springdoc.core.customizers.OpenApiBuilderCustomizer;
|
import org.springdoc.core.customizers.OpenApiBuilderCustomizer;
|
||||||
import org.springdoc.core.customizers.ServerBaseUrlCustomizer;
|
import org.springdoc.core.customizers.ServerBaseUrlCustomizer;
|
||||||
import org.springdoc.core.properties.SpringDocConfigProperties;
|
import org.springdoc.core.properties.SpringDocConfigProperties;
|
||||||
@@ -86,9 +85,9 @@ public class OpenApiHandler extends OpenAPIService {
|
|||||||
private final PropertyResolverUtils propertyResolverUtils;
|
private final PropertyResolverUtils propertyResolverUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 权限元数据解析器接口
|
* Javadoc解析器接口
|
||||||
*/
|
*/
|
||||||
private final SaTokenMetadataResolver saTokenJavadocResolver;
|
private final List<JavadocResolver> javadocResolvers;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The javadoc provider.
|
* The javadoc provider.
|
||||||
@@ -131,7 +130,7 @@ public class OpenApiHandler extends OpenAPIService {
|
|||||||
Optional<List<OpenApiBuilderCustomizer>> openApiBuilderCustomizers,
|
Optional<List<OpenApiBuilderCustomizer>> openApiBuilderCustomizers,
|
||||||
Optional<List<ServerBaseUrlCustomizer>> serverBaseUrlCustomizers,
|
Optional<List<ServerBaseUrlCustomizer>> serverBaseUrlCustomizers,
|
||||||
Optional<JavadocProvider> javadocProvider,
|
Optional<JavadocProvider> javadocProvider,
|
||||||
SaTokenMetadataResolver saTokenJavadocResolver) {
|
List<JavadocResolver> javadocResolvers) {
|
||||||
super(openAPI, securityParser, springDocConfigProperties, propertyResolverUtils, openApiBuilderCustomizers, serverBaseUrlCustomizers, javadocProvider);
|
super(openAPI, securityParser, springDocConfigProperties, propertyResolverUtils, openApiBuilderCustomizers, serverBaseUrlCustomizers, javadocProvider);
|
||||||
if (openAPI.isPresent()) {
|
if (openAPI.isPresent()) {
|
||||||
this.openAPI = openAPI.get();
|
this.openAPI = openAPI.get();
|
||||||
@@ -148,7 +147,7 @@ public class OpenApiHandler extends OpenAPIService {
|
|||||||
this.openApiBuilderCustomisers = openApiBuilderCustomizers;
|
this.openApiBuilderCustomisers = openApiBuilderCustomizers;
|
||||||
this.serverBaseUrlCustomizers = serverBaseUrlCustomizers;
|
this.serverBaseUrlCustomizers = serverBaseUrlCustomizers;
|
||||||
this.javadocProvider = javadocProvider;
|
this.javadocProvider = javadocProvider;
|
||||||
this.saTokenJavadocResolver = saTokenJavadocResolver;
|
this.javadocResolvers = javadocResolvers == null ? new ArrayList<>() : javadocResolvers;
|
||||||
if (springDocConfigProperties.isUseFqn())
|
if (springDocConfigProperties.isUseFqn())
|
||||||
TypeNameResolver.std.setUseFqn(true);
|
TypeNameResolver.std.setUseFqn(true);
|
||||||
}
|
}
|
||||||
@@ -235,16 +234,14 @@ public class OpenApiHandler extends OpenAPIService {
|
|||||||
if (StringUtils.isNotBlank(description)){
|
if (StringUtils.isNotBlank(description)){
|
||||||
operation.setSummary(summary);
|
operation.setSummary(summary);
|
||||||
}
|
}
|
||||||
// 调用SaToken解析器提取JavaDoc中的权限信息
|
// 调用解析器提取JavaDoc中的权限信息
|
||||||
if (saTokenJavadocResolver.supports(handlerMethod)) {
|
if (javadocResolvers != null && !javadocResolvers.isEmpty()) {
|
||||||
SaTokenSecurityMetadata metadata = new SaTokenSecurityMetadata();
|
for (JavadocResolver resolver : javadocResolvers) {
|
||||||
saTokenJavadocResolver.resolve(handlerMethod, operation, metadata);
|
String desc = resolver.resolve(handlerMethod, operation);
|
||||||
String markdownString = metadata.toMarkdownString();
|
description = description + desc;
|
||||||
if (StringUtils.isNotBlank(markdownString)) {
|
|
||||||
description = description + markdownString;
|
|
||||||
}
|
}
|
||||||
|
operation.setDescription(description);
|
||||||
}
|
}
|
||||||
operation.setDescription(description);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return operation;
|
return operation;
|
||||||
|
|||||||
Reference in New Issue
Block a user