通过自定义注解加aop切面实现权限控制

发布于:2025-08-07 ⋅ 阅读:(16) ⋅ 点赞:(0)

前言:自定义注解,通过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);
	}

注:将注解加入接口处进行调用(当接口被调用时,前置通知进行拦截判断权限)


网站公告

今日签到

点亮在社区的每一天
去签到