前言:自定义注解,通过aop切面前置通知,对请求接口进行权限控制
1,创建枚举类
package org.springblade.sample.annotationCommon;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.Arrays;
import java.util.Optional;
/**
* @Title: PermissionAnnotationEnum
* @Author it—xtm
* @Package AnnotationCommon
* @Date 2025/8/5 21:21
* @description: 权限枚举类,定义系统中常用的权限控制类型
*/
@Getter
@AllArgsConstructor
public enum PermissionAnnotationEnum {
/**
* 全部权限:可以查看所有数据
*/
ALL(1, "全部数据可见"),
/**
* 仅本人可见:只能查看自己创建的数据
*/
OWN(2, "仅本人可见"),
/**
* 本部门可见:只能查看本部门数据
*/
OWN_DEPT(3, "所在机构可见"),
/**
* 本部门及子部门可见
*/
OWN_DEPT_CHILD(4, "所在机构及子级机构可见"),
/**
* 自定义权限:根据自定义条件过滤数据
*/
CUSTOM(5, "自定义权限范围"),
/**
* 无权限:不能查看任何数据
*/
NONE(6, "无权限访问");
/**
* 权限类型编码
*/
private final Integer type;
/**
* 权限描述
*/
private final String description;
/**
* 根据类型编码获取枚举实例
*
* @param type 权限类型编码
* @return 对应的枚举实例,若不存在则返回空
*/
public static PermissionAnnotationEnum getByType(Integer type) {
if (type == null) {
return null;
}
return Arrays.stream(values())
.filter(enumItem -> enumItem.getType().equals(type)).findFirst().orElse(null);
}
}
2,创建注解
package org.springblade.sample.annotationCommon;
import java.lang.annotation.*;
/**
* @Title: PermissionAnnotationEnum
* @Author it—xtm
* @Package AnnotationCommon
* @Date 2025/8/5 21:21
* @description: 权限注解
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Inherited // 允许注解被子类继承
@Documented // 生成JavaDoc时会包含该注解说明
public @interface PermissionAnnotation {
PermissionAnnotationEnum type() default PermissionAnnotationEnum.ALL; //权限 类型
String[] menuValue();// 需要的菜单编号标识
String apiValue();// 需要的api标识
boolean isIgnoreRole() default false;// 是否忽略
String[] ignoreRoleValue() default {"administrator", "admin"}; //管理员直接忽略
}
3,创建切面
package org.springblade.sample.annotationCommon;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
import java.util.Arrays;
/**
* @Title: AnnotationCommon.AnnotationAspect
* @Author it-xtm
* @Package PACKAGE_NAME
* @Date 2025/8/5 21:45
* @description: 权限注解切面,包含各种通知类型
*/
@Aspect
@Component
@Slf4j
public class AnnotationAspect {
/**
* 方法执行前执行 - 前置通知
* @param joinPoint 切入点对象,提供了关于当前执行方法的信息
* @param permissionAnnotation 注解对象,包含了注解的属性值
*/
@Before("@annotation(permissionAnnotation)")
public void before(JoinPoint joinPoint, PermissionAnnotation permissionAnnotation) {
log.info("===== 前置通知开始 =====");
log.info("目标方法: {}.{}",
joinPoint.getTarget().getClass().getName(),
joinPoint.getSignature().getName());
log.info("方法参数:{}", Arrays.toString(permissionAnnotation.menuValue()));
log.info("方法参数:{}", permissionAnnotation.type());
log.info("方法参数:{}", permissionAnnotation.apiValue());
log.info("方法参数:{}", Arrays.toString(permissionAnnotation.ignoreRoleValue()));
log.info("方法参数:{}", permissionAnnotation.isIgnoreRole());
log.info("方法参数:{}", permissionAnnotation.type().getType());
log.info("方法参数:{}", permissionAnnotation.type().getDescription());
log.info("方法参数:{}", PermissionAnnotationEnum.getByType(permissionAnnotation.type().getType()).getDescription());
log.info("===== 前置通知结束 =====");
}
/**
* 环绕通知 - 可以控制目标方法的执行
* @param proceedingJoinPoint 可执行的切入点对象
* @param permissionAnnotation 注解对象
* @return 目标方法的返回值
* @throws Throwable 可能抛出的异常
*/
@Around("@annotation(permissionAnnotation)")
public Object around(ProceedingJoinPoint proceedingJoinPoint, PermissionAnnotation permissionAnnotation) throws Throwable {
log.info("===== 环绕通知开始 =====");
log.info("环绕通知 - 执行目标方法前");
// 可以在这里进行权限验证等逻辑
boolean hasPermission = checkPermission(permissionAnnotation);
if (!hasPermission) {
log.warn("权限不足,无法执行方法: {}", proceedingJoinPoint.getSignature().getName());
throw new SecurityException("没有执行该操作的权限");
}
// 执行目标方法
long startTime = System.currentTimeMillis();
Object result = proceedingJoinPoint.proceed(); // 执行目标方法
long endTime = System.currentTimeMillis();
log.info("环绕通知 - 执行目标方法后");
log.info("方法执行耗时: {}ms", (endTime - startTime));
log.info("===== 环绕通知结束 =====");
return result;
}
/**
* 后置通知 - 无论方法是否正常执行都会执行
* @param joinPoint 切入点对象
* @param permissionAnnotation 注解对象
*/
@After("@annotation(permissionAnnotation)")
public void after(JoinPoint joinPoint, PermissionAnnotation permissionAnnotation) {
log.info("===== 后置通知开始 =====");
log.info("目标方法: {}.{} 执行完成",
joinPoint.getTarget().getClass().getName(),
joinPoint.getSignature().getName());
log.info("清理资源或记录日志等操作");
log.info("===== 后置通知结束 =====");
}
/**
* 返回后通知 - 方法正常返回后执行
* @param joinPoint 切入点对象
* @param permissionAnnotation 注解对象
* @param result 方法返回值
*/
@AfterReturning(pointcut = "@annotation(permissionAnnotation)", returning = "result")
public void afterReturning(JoinPoint joinPoint, PermissionAnnotation permissionAnnotation, Object result) {
log.info("===== 返回后通知开始 =====");
log.info("目标方法: {}.{} 正常返回",
joinPoint.getTarget().getClass().getName(),
joinPoint.getSignature().getName());
log.info("方法返回值: {}", result);
log.info("可以在这里处理返回结果");
log.info("===== 返回后通知结束 =====");
}
/**
* 异常通知 - 方法抛出异常时执行
* @param joinPoint 切入点对象
* @param permissionAnnotation 注解对象
* @param ex 抛出的异常
*/
@AfterThrowing(pointcut = "@annotation(permissionAnnotation)", throwing = "ex")
public void afterThrowing(JoinPoint joinPoint, PermissionAnnotation permissionAnnotation, Exception ex) {
log.error("===== 异常通知开始 =====", ex);
log.error("目标方法: {}.{} 抛出异常",
joinPoint.getTarget().getClass().getName(),
joinPoint.getSignature().getName());
log.error("异常信息: {}", ex.getMessage());
log.error("可以在这里记录异常日志或进行异常处理");
log.error("===== 异常通知结束 =====");
}
/**
* 权限检查逻辑
* @param permissionAnnotation 权限注解
* @return 是否有权限
*/
private boolean checkPermission(PermissionAnnotation permissionAnnotation) {
// 实际应用中这里应该实现真实的权限检查逻辑
log.info("执行权限检查: {}", permissionAnnotation.apiValue());
// 简单示例:默认有权限
return true;
}
}
4,实现示例
@GetMapping("/list")
@ApiOperationSupport(order = 2)
@ApiOperation(value = "分页", notes = "参数")
@PermissionAnnotation(menuValue = {"test","test2"}, apiValue = "annotation_test")
public R<IPage<>> list() {
return R.data(null);
}
注:将注解加入接口处进行调用(当接口被调用时,前置通知进行拦截判断权限)