mirror of
https://gitee.com/dromara/RuoYi-Vue-Plus.git
synced 2026-03-07 23:31:09 +08:00
!835 update Sa-Token 权限码展示,接口详情显示权限码,感谢nextdoc4j
* update 增强接口描述,合并JavaDoc权限信息到操作描述中 * update 优化satoken依赖引用,减少耦合性 * update 优化接口描述文本展示 * update Sa-Token 权限码展示,接口详情显示权限码,感谢nextdoc4j
This commit is contained in:
@@ -21,6 +21,11 @@
|
||||
<artifactId>ruoyi-common-core</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-core</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springdoc</groupId>
|
||||
<artifactId>springdoc-openapi-starter-webmvc-api</artifactId>
|
||||
|
||||
@@ -7,6 +7,8 @@ import io.swagger.v3.oas.models.security.SecurityRequirement;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.dromara.common.core.utils.StringUtils;
|
||||
import org.dromara.common.doc.config.properties.SpringDocProperties;
|
||||
import org.dromara.common.doc.core.enhancer.SaTokenJavadocResolver;
|
||||
import org.dromara.common.doc.core.enhancer.SaTokenMetadataResolver;
|
||||
import org.dromara.common.doc.handler.OpenApiHandler;
|
||||
import org.springdoc.core.configuration.SpringDocConfiguration;
|
||||
import org.springdoc.core.customizers.OpenApiBuilderCustomizer;
|
||||
@@ -84,8 +86,9 @@ public class SpringDocConfig {
|
||||
SecurityService securityParser,
|
||||
SpringDocConfigProperties springDocConfigProperties, PropertyResolverUtils propertyResolverUtils,
|
||||
Optional<List<OpenApiBuilderCustomizer>> openApiBuilderCustomisers,
|
||||
Optional<List<ServerBaseUrlCustomizer>> serverBaseUrlCustomisers, Optional<JavadocProvider> javadocProvider) {
|
||||
return new OpenApiHandler(openAPI, securityParser, springDocConfigProperties, propertyResolverUtils, openApiBuilderCustomisers, serverBaseUrlCustomisers, javadocProvider);
|
||||
Optional<List<ServerBaseUrlCustomizer>> serverBaseUrlCustomisers, Optional<JavadocProvider> javadocProvider,
|
||||
SaTokenMetadataResolver saTokenMetadataResolver) {
|
||||
return new OpenApiHandler(openAPI, securityParser, springDocConfigProperties, propertyResolverUtils, openApiBuilderCustomisers, serverBaseUrlCustomisers, javadocProvider, saTokenMetadataResolver);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -112,6 +115,14 @@ public class SpringDocConfig {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册JavaDoc权限解析器
|
||||
*/
|
||||
@Bean
|
||||
public SaTokenMetadataResolver saTokenJavadocResolver() {
|
||||
return new SaTokenJavadocResolver();
|
||||
}
|
||||
|
||||
/**
|
||||
* 单独使用一个类便于判断 解决springdoc路径拼接重复问题
|
||||
*
|
||||
|
||||
@@ -0,0 +1,200 @@
|
||||
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 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 = convertToStringArray(value);
|
||||
String modeStr = mode != null ? mode.toString() : "AND";
|
||||
String typeStr = type != null ? type.toString() : "";
|
||||
String[] orRoles = convertToStringArray(orRole);
|
||||
|
||||
metadata.addPermission(values, modeStr, typeStr, orRoles);
|
||||
} catch (Exception e) {
|
||||
// 忽略解析错误
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析角色校验
|
||||
*/
|
||||
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 = convertToStringArray(value);
|
||||
String modeStr = mode != null ? mode.toString() : "AND";
|
||||
String typeStr = type != null ? type.toString() : "";
|
||||
|
||||
metadata.addRole(values, modeStr, typeStr);
|
||||
} catch (Exception e) {
|
||||
// 忽略解析错误
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查注解是否存在
|
||||
*/
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换为字符串数组
|
||||
*/
|
||||
private String[] convertToStringArray(Object value) {
|
||||
if (value == null) {
|
||||
return new String[0];
|
||||
}
|
||||
if (value instanceof String[]) {
|
||||
return (String[])value;
|
||||
}
|
||||
if (value instanceof String) {
|
||||
return new String[] {(String)value};
|
||||
}
|
||||
return new String[0];
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
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,175 @@
|
||||
package org.dromara.common.doc.core.model;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude.Include;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 存储权限框架注解解析后的权限和角色信息
|
||||
*
|
||||
* @author AprilWind
|
||||
*/
|
||||
@Data
|
||||
@JsonInclude(Include.NON_EMPTY)
|
||||
public class SaTokenSecurityMetadata {
|
||||
|
||||
/**
|
||||
* 权限校验信息列表(对应 @SaCheckPermission 注解)
|
||||
*/
|
||||
private List<AuthInfo> permissions = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* 角色校验信息列表(对应 @SaCheckRole 注解)
|
||||
*/
|
||||
private List<AuthInfo> roles = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* 是否忽略校验(对应 @SaIgnore 注解)
|
||||
*/
|
||||
private boolean ignore = false;
|
||||
|
||||
/**
|
||||
* 添加权限信息
|
||||
*
|
||||
* @param values 权限值数组
|
||||
* @param mode 校验模式(AND/OR)
|
||||
* @param type 权限类型
|
||||
* @param orRoles 或角色数组
|
||||
*/
|
||||
public void addPermission(String[] values, String mode, String type, String[] orRoles) {
|
||||
if (values != null && values.length > 0) {
|
||||
AuthInfo authInfo = new AuthInfo();
|
||||
authInfo.setValues(values);
|
||||
authInfo.setMode(mode);
|
||||
authInfo.setType(type);
|
||||
if (orRoles != null && orRoles.length > 0) {
|
||||
authInfo.setOrValues(orRoles);
|
||||
authInfo.setOrType("role");
|
||||
}
|
||||
this.permissions.add(authInfo);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加角色信息
|
||||
*
|
||||
* @param values 角色值数组
|
||||
* @param mode 校验模式(AND/OR)
|
||||
* @param type 角色类型
|
||||
*/
|
||||
public void addRole(String[] values, String mode, String type) {
|
||||
if (values != null && values.length > 0) {
|
||||
AuthInfo authInfo = new AuthInfo();
|
||||
authInfo.setValues(values);
|
||||
authInfo.setMode(mode);
|
||||
authInfo.setType(type);
|
||||
this.roles.add(authInfo);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成 Markdown 结构的权限说明
|
||||
*
|
||||
* @return Markdown 文本
|
||||
*/
|
||||
public String toMarkdownString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("<br><h3>访问权限</h3><br>");
|
||||
|
||||
if (ignore) {
|
||||
sb.append("> **权限策略**:忽略权限检查<br>");
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
if (!ignore && permissions.isEmpty() && roles.isEmpty()){
|
||||
sb.append("> **权限策略**:需要登录<br><br>");
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
if (!permissions.isEmpty()) {
|
||||
sb.append("**权限校验:**<br><br>");
|
||||
|
||||
permissions.forEach(p -> {
|
||||
String permTags = Arrays.stream(p.getValues())
|
||||
.map(v -> "`" + v + "`")
|
||||
.collect(Collectors.joining(p.getModeSymbol()));
|
||||
|
||||
sb.append("- ").append(permTags).append("<br>");
|
||||
|
||||
if (p.getOrValues() != null && p.getOrValues().length > 0) {
|
||||
String orTags = Arrays.stream(p.getOrValues())
|
||||
.map(v -> "`" + v + "`")
|
||||
.collect(Collectors.joining(p.getModeSymbol()));
|
||||
sb.append(" - 或角色:").append(orTags).append("<br>");
|
||||
}
|
||||
});
|
||||
|
||||
sb.append("<br>");
|
||||
}
|
||||
|
||||
if (!roles.isEmpty()) {
|
||||
sb.append("**角色校验:**<br><br>");
|
||||
|
||||
roles.forEach(r -> {
|
||||
|
||||
String roleTags = Arrays.stream(r.getValues())
|
||||
.map(v -> "`" + v + "`")
|
||||
.collect(Collectors.joining(r.getModeSymbol()));
|
||||
|
||||
sb.append("- ").append(roleTags).append("<br>");
|
||||
});
|
||||
}
|
||||
|
||||
return sb.toString().trim();
|
||||
}
|
||||
|
||||
/**
|
||||
* 认证信息
|
||||
*/
|
||||
@Data
|
||||
@JsonInclude(Include.NON_EMPTY)
|
||||
public static class AuthInfo {
|
||||
|
||||
/**
|
||||
* 权限或角色值数组
|
||||
*/
|
||||
private String[] values;
|
||||
|
||||
/**
|
||||
* 校验模式(AND/OR)
|
||||
*/
|
||||
private String mode;
|
||||
|
||||
/**
|
||||
* 类型说明
|
||||
*/
|
||||
private String type;
|
||||
|
||||
/**
|
||||
* 或权限/角色值数组(用于权限校验时的或角色校验)
|
||||
*/
|
||||
private String[] orValues;
|
||||
|
||||
/**
|
||||
* 或值的类型(role/permission)
|
||||
*/
|
||||
private String orType;
|
||||
|
||||
/**
|
||||
* 重写mode的获取方法,返回符号而非文字
|
||||
* @return AND→&,OR→|,默认→&
|
||||
*/
|
||||
public String getModeSymbol() {
|
||||
if (mode == null) {
|
||||
return " & "; // 默认AND,返回&
|
||||
}
|
||||
return "AND".equalsIgnoreCase(mode) ? " & " : " | ";
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -12,6 +12,8 @@ import io.swagger.v3.oas.models.tags.Tag;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.dromara.common.core.utils.StreamUtils;
|
||||
import org.dromara.common.doc.core.enhancer.SaTokenMetadataResolver;
|
||||
import org.dromara.common.doc.core.model.SaTokenSecurityMetadata;
|
||||
import org.springdoc.core.customizers.OpenApiBuilderCustomizer;
|
||||
import org.springdoc.core.customizers.ServerBaseUrlCustomizer;
|
||||
import org.springdoc.core.properties.SpringDocConfigProperties;
|
||||
@@ -83,6 +85,11 @@ public class OpenApiHandler extends OpenAPIService {
|
||||
*/
|
||||
private final PropertyResolverUtils propertyResolverUtils;
|
||||
|
||||
/**
|
||||
* 权限元数据解析器接口
|
||||
*/
|
||||
private final SaTokenMetadataResolver saTokenJavadocResolver;
|
||||
|
||||
/**
|
||||
* The javadoc provider.
|
||||
*/
|
||||
@@ -123,7 +130,8 @@ public class OpenApiHandler extends OpenAPIService {
|
||||
SpringDocConfigProperties springDocConfigProperties, PropertyResolverUtils propertyResolverUtils,
|
||||
Optional<List<OpenApiBuilderCustomizer>> openApiBuilderCustomizers,
|
||||
Optional<List<ServerBaseUrlCustomizer>> serverBaseUrlCustomizers,
|
||||
Optional<JavadocProvider> javadocProvider) {
|
||||
Optional<JavadocProvider> javadocProvider,
|
||||
SaTokenMetadataResolver saTokenJavadocResolver) {
|
||||
super(openAPI, securityParser, springDocConfigProperties, propertyResolverUtils, openApiBuilderCustomizers, serverBaseUrlCustomizers, javadocProvider);
|
||||
if (openAPI.isPresent()) {
|
||||
this.openAPI = openAPI.get();
|
||||
@@ -140,6 +148,7 @@ public class OpenApiHandler extends OpenAPIService {
|
||||
this.openApiBuilderCustomisers = openApiBuilderCustomizers;
|
||||
this.serverBaseUrlCustomizers = serverBaseUrlCustomizers;
|
||||
this.javadocProvider = javadocProvider;
|
||||
this.saTokenJavadocResolver = saTokenJavadocResolver;
|
||||
if (springDocConfigProperties.isUseFqn())
|
||||
TypeNameResolver.std.setUseFqn(true);
|
||||
}
|
||||
@@ -219,7 +228,21 @@ public class OpenApiHandler extends OpenAPIService {
|
||||
else
|
||||
securityParser.buildSecurityRequirement(securityRequirements, operation);
|
||||
}
|
||||
|
||||
String description = javadocProvider.get().getMethodJavadocDescription(handlerMethod.getMethod());
|
||||
String summary = javadocProvider.get().getFirstSentence(description);
|
||||
if (StringUtils.isNotBlank(description)){
|
||||
operation.setSummary(summary);
|
||||
}
|
||||
// 调用SaToken解析器提取JavaDoc中的权限信息
|
||||
if (saTokenJavadocResolver.supports(handlerMethod)) {
|
||||
SaTokenSecurityMetadata metadata = new SaTokenSecurityMetadata();
|
||||
saTokenJavadocResolver.resolve(handlerMethod, operation, metadata);
|
||||
String markdownString = metadata.toMarkdownString();
|
||||
if (StringUtils.isNotBlank(markdownString)) {
|
||||
description = description + markdownString;
|
||||
}
|
||||
}
|
||||
operation.setDescription(description);
|
||||
return operation;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,198 @@
|
||||
package org.dromara.demo.controller;
|
||||
|
||||
import cn.dev33.satoken.annotation.*;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.common.core.domain.R;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
* SaToken 权限测试
|
||||
*
|
||||
* @author AprilWind
|
||||
*/
|
||||
@Slf4j
|
||||
@RestController
|
||||
@RequestMapping("/demo/SaToken")
|
||||
public class SaTokenTestController {
|
||||
|
||||
// ====================== 基础场景:单一校验规则 ======================
|
||||
|
||||
/**
|
||||
* 场景1:仅登录校验(无角色/权限限制,只需登录态)
|
||||
*/
|
||||
@SaCheckLogin
|
||||
@GetMapping("/basic/loginOnly")
|
||||
public R<Void> loginOnly() {
|
||||
log.info("【场景1】仅登录校验通过");
|
||||
return R.ok("仅登录校验通过,无需角色/权限");
|
||||
}
|
||||
|
||||
/**
|
||||
* 场景2:单一角色校验(AND模式,默认)
|
||||
*/
|
||||
@SaCheckRole("admin")
|
||||
@GetMapping("/basic/singleRole")
|
||||
public R<Void> singleRole() {
|
||||
log.info("【场景2】单一角色(admin)校验通过");
|
||||
return R.ok("拥有admin角色,校验通过");
|
||||
}
|
||||
|
||||
/**
|
||||
* 场景3:单一权限校验(AND模式,默认)
|
||||
*/
|
||||
@SaCheckPermission("system:user:view")
|
||||
@GetMapping("/basic/singlePermission")
|
||||
public R<Void> singlePermission() {
|
||||
log.info("【场景3】单一权限(system:user:view)校验通过");
|
||||
return R.ok("拥有system:user:view权限,校验通过");
|
||||
}
|
||||
|
||||
/**
|
||||
* 场景4:忽略所有权限校验(SaIgnore优先级最高)
|
||||
*/
|
||||
@SaIgnore
|
||||
@SaCheckRole("none_exist") // 该注解会被忽略
|
||||
@GetMapping("/basic/ignoreAll")
|
||||
public R<Void> ignoreAll() {
|
||||
log.info("【场景4】SaIgnore忽略所有权限校验");
|
||||
return R.ok("SaIgnore生效,所有权限校验被忽略");
|
||||
}
|
||||
|
||||
// ====================== 进阶场景:多条件组合(AND/OR) ======================
|
||||
|
||||
/**
|
||||
* 场景5:多角色AND模式(必须同时拥有所有角色)
|
||||
*/
|
||||
@SaCheckRole(value = {"admin", "operator"}, mode = SaMode.AND)
|
||||
@GetMapping("/advance/multiRoleAnd")
|
||||
public R<Void> multiRoleAnd() {
|
||||
log.info("【场景5】多角色AND模式(admin+operator)校验通过");
|
||||
return R.ok("同时拥有admin和operator角色,校验通过");
|
||||
}
|
||||
|
||||
/**
|
||||
* 场景6:多角色OR模式(拥有任一角色即可)
|
||||
*/
|
||||
@SaCheckRole(value = {"admin", "test"}, mode = SaMode.OR)
|
||||
@GetMapping("/advance/multiRoleOr")
|
||||
public R<Void> multiRoleOr() {
|
||||
log.info("【场景6】多角色OR模式(admin|test)校验通过");
|
||||
return R.ok("拥有admin或test角色,校验通过");
|
||||
}
|
||||
|
||||
/**
|
||||
* 场景7:多权限AND模式(必须同时拥有所有权限)
|
||||
*/
|
||||
@SaCheckPermission(value = {"system:user:edit", "system:log:view"}, mode = SaMode.AND)
|
||||
@GetMapping("/advance/multiPermAnd")
|
||||
public R<Void> multiPermAnd() {
|
||||
log.info("【场景7】多权限AND模式(system:user:edit+system:log:view)校验通过");
|
||||
return R.ok("同时拥有system:user:edit和system:log:view权限,校验通过");
|
||||
}
|
||||
|
||||
/**
|
||||
* 场景8:多权限OR模式(拥有任一权限即可)
|
||||
*/
|
||||
@SaCheckPermission(value = {"system:user:add", "system:user:delete"}, mode = SaMode.OR)
|
||||
@GetMapping("/advance/multiPermOr")
|
||||
public R<Void> multiPermOr() {
|
||||
log.info("【场景8】多权限OR模式(system:user:add|system:user:delete)校验通过");
|
||||
return R.ok("拥有system:user:add或system:user:delete权限,校验通过");
|
||||
}
|
||||
|
||||
// ====================== 高级场景:通配符/混合组合 ======================
|
||||
|
||||
/**
|
||||
* 场景9:权限通配符匹配(前缀匹配)
|
||||
* 拥有system:user:* 即可匹配所有用户模块权限
|
||||
*/
|
||||
@SaCheckPermission("system:user:*")
|
||||
@GetMapping("/advanced/permWildcardPrefix")
|
||||
public R<Void> permWildcardPrefix() {
|
||||
log.info("【场景9】权限通配符(system:user:*)校验通过");
|
||||
return R.ok("拥有system:user:*前缀权限,校验通过");
|
||||
}
|
||||
|
||||
/**
|
||||
* 场景10:角色通配符匹配(前缀匹配)
|
||||
* 拥有admin_* 即可匹配所有admin开头的角色
|
||||
*/
|
||||
@SaCheckRole("admin_*")
|
||||
@GetMapping("/advanced/roleWildcardPrefix")
|
||||
public R<Void> roleWildcardPrefix() {
|
||||
log.info("【场景10】角色通配符(admin_*)校验通过");
|
||||
return R.ok("拥有admin_*前缀角色,校验通过");
|
||||
}
|
||||
|
||||
/**
|
||||
* 场景11:权限+角色混合AND模式(所有条件必须满足)
|
||||
* 需同时满足:拥有admin角色 + 拥有system:user:all权限
|
||||
*/
|
||||
@SaCheckRole("admin")
|
||||
@SaCheckPermission("system:user:all")
|
||||
@GetMapping("/advanced/mixRolePermAnd")
|
||||
public R<Void> mixRolePermAnd() {
|
||||
log.info("【场景11】角色+权限混合AND(admin+system:user:all)校验通过");
|
||||
return R.ok("拥有admin角色且拥有system:user:all权限,校验通过");
|
||||
}
|
||||
|
||||
/**
|
||||
* 场景12:权限+角色混合OR模式(任一条件满足即可)
|
||||
* 满足任一:拥有super_admin角色 | 拥有system:manage权限
|
||||
*/
|
||||
@SaCheckRole(value = {"super_admin"}, mode = SaMode.OR)
|
||||
@SaCheckPermission(value = {"system:manage"}, mode = SaMode.OR)
|
||||
@GetMapping("/advanced/mixRolePermOr")
|
||||
public R<Void> mixRolePermOr() {
|
||||
log.info("【场景12】角色+权限混合OR(super_admin|system:manage)校验通过");
|
||||
return R.ok("拥有super_admin角色或system:manage权限,校验通过");
|
||||
}
|
||||
|
||||
/**
|
||||
* 场景13:orRole参数(权限校验失败时,兜底角色校验)
|
||||
* 核心逻辑:无system:user:export权限时,检查是否有admin/operator角色
|
||||
*/
|
||||
@SaCheckPermission(value = "system:user:export", orRole = {"admin", "operator"})
|
||||
@GetMapping("/advanced/permWithOrRole")
|
||||
public R<Void> permWithOrRole() {
|
||||
log.info("【场景13】权限+orRole兜底校验通过");
|
||||
return R.ok("拥有system:user:export权限,或拥有admin/operator角色,校验通过");
|
||||
}
|
||||
|
||||
// ====================== 特殊场景:临时权限/注解覆盖 ======================
|
||||
|
||||
/**
|
||||
* 场景14:SaIgnore局部覆盖(方法注解覆盖类注解,若有)
|
||||
* 假设类上有@SaCheckLogin,方法上@SaIgnore会覆盖
|
||||
*/
|
||||
@SaIgnore
|
||||
@GetMapping("/special/ignoreOverride")
|
||||
public R<Void> ignoreOverride() {
|
||||
log.info("【场景14】SaIgnore覆盖类级别权限注解");
|
||||
return R.ok("方法级SaIgnore覆盖类级别权限校验");
|
||||
}
|
||||
|
||||
/**
|
||||
* 场景15:临时权限校验(SaCheckPermission逻辑:临时权限>永久权限)
|
||||
* 注:临时权限需通过SaToken API手动设置,如 SaHolder.getStpLogic().setTempPermission("system:temp:test")
|
||||
*/
|
||||
@SaCheckPermission("system:temp:test")
|
||||
@GetMapping("/special/tempPermission")
|
||||
public R<Void> tempPermission() {
|
||||
log.info("【场景15】临时权限(system:temp:test)校验通过");
|
||||
return R.ok("临时权限校验通过(需先通过API设置临时权限)");
|
||||
}
|
||||
|
||||
/**
|
||||
* 场景16:登录类型指定(多端登录场景,如PC/APP/小程序)
|
||||
* 注:需配合SaToken多账号体系配置
|
||||
*/
|
||||
@SaCheckLogin(type = "PC") // 仅校验PC端的登录态
|
||||
@GetMapping("/special/loginTypeSpecify")
|
||||
public R<Void> loginTypeSpecify() {
|
||||
log.info("【场景16】指定登录类型(PC)校验通过");
|
||||
return R.ok("仅PC端登录态校验通过");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user