侧边栏壁纸
  • 累计撰写 27 篇文章
  • 累计创建 42 个标签
  • 累计收到 33 条评论

目 录CONTENT

文章目录

自定义注解+AOP实现权限管理

miykah
2023-06-30 / 3 评论 / 0 点赞 / 54 阅读 / 6180 字 / 正在检测是否收录...
温馨提示:
本文最后更新于 2023-12-14,若内容或图片失效,请留言反馈。部分素材来自网络,若不小心影响到您的利益,请联系我们删除。

权限管理注解 @AuthCheck 及 AOP

使用方法

在需要校验权限的方法(控制器方法)上,加上 @AuthCheck 注解并指定该方法需要什么角色才能访问。

image-20230526134510324

如果没有权限进行操作,就会抛出异常。

比如:

  • 在方法上使用注解(anyRole)

@AuthCheck(anyRole = {"admin", "user"})

最终在AuthInterceptor中,会校验,如果当前登录用户是 anyRole 数组中任一角色,即可访问被这个注解注解的方法。

  • 在方法上使用注解(mustRole)

@AuthCheck(mustRole = "admin")

最终在AuthInterceptor中,会校验,如果当前登录用户是 mustRole 指定的角色,即可访问被这个注解注解的方法。

源码学习

AuthCheck 注解

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AuthCheck {
​
    /**
     * 有任何一个角色
     *
     * @return
     */
    String[] anyRole() default "";
​
    /**
     * 必须有某个角色
     *
     * @return
     */
    String mustRole() default "";
​
}

一篇文章,全面掌握Java自定义注解(Annontation)

注解的定义
  • 注解的定义通过@interface表示,所有的注解会自动继承java.lang.Annotation接口,且不能再继承别的类或是接口。

  • 注解的成员参数只能用public或默认(default) 访问权修饰来进行修饰。

  • 成员参数只能使用八种基本类型(byte、short、char、int、long、float、double、boolean)和String、Enum、Class、annotations等数据类型,及其数组。

  • 获取类方法和字段的注解信息,只能通过Java的反射技术来获取 Annotation 对象。

  • 注解可以没有定义成员,只做标识。

元注解

**@Target****@Retention**是元注解(注解其他注解的注解)

  • @Documented – 注解是否将包含在JavaDoc中

  • @Retention – 注解的生命周期

  • @Target – 注解用于什么地方

  • @Inherited – 是否允许子类继承该注解

  • @Repeatable - 是否可重复注解,jdk1.8引入

注解的生命周期

通过@Retention定义注解的生命周期,格式如下:

@Retention(RetentionPolicy.SOURCE)

其中RetentionPolicy的不同策略对应的生命周期如下:

  • RetentionPolicy.SOURCE : 仅存在于源代码中,编译阶段会被丢弃,不会包含于class字节码文件中。@Override, @SuppressWarnings都属于这类注解。

  • RetentionPolicy.CLASS : 默认策略,在class字节码文件中存在,在类加载的时被丢弃,运行时无法获取到。

  • RetentionPolicy.RUNTIME : 始终不会丢弃,可以使用反射获得该注解的信息。自定义的注解最常用的使用方式。

注解的作用目标

通过@Target定义注解作用的目标,比如作用于类、属性、或方法等,默认可用于任何地方。格式如下:

@Target(ElementType.TYPE)

对应ElementType参数值适用范围如下:

  • ElementType.TYPE: 类、接口、注解、enum

  • ElementType.CONSTRUCTOR: 构造函数

  • ElementType.FIELD: 成员变量、对象、属性、枚举的常量

  • ElementType.LOCAL_VARIABLE: 局部变量

  • ElementType.METHOD: 方法

  • ElementType.PACKAGE: 包

  • ElementType.PARAMETER: 参数

  • ElementType.ANNOTATION_TYPE): 注解

  • ElementType.TYPE_PARAMETER:类型参数,表示这个注解可以用在 Type的声明式前,jdk1.8引入。

  • ElementType.TYPE_USE:类型的注解,表示这个注解可以用在所有使用Type的地方(如:泛型,类型转换等),jdk1.8引入。

AOP 实现权限管理拦截器

@Aspect
@Component
public class AuthInterceptor {
​
    @Resource
    private UserService userService;
​
    /**
     * 执行拦截
     *
     * @param joinPoint
     * @param authCheck
     * @return
     */
    @Around("@annotation(authCheck)")
    public Object doInterceptor(ProceedingJoinPoint joinPoint, AuthCheck authCheck) throws Throwable {
        // 获取注解的 anyRole数组, 将其转换为 List
        List<String> anyRole = Arrays.stream(authCheck.anyRole()).filter(StringUtils::isNotBlank).collect(Collectors.toList());
        // 获取注解的 mustRole
        String mustRole = authCheck.mustRole();
        // 获取当前登录用户
        RequestAttributes requestAttributes = RequestContextHolder.currentRequestAttributes();
        HttpServletRequest request = ((ServletRequestAttributes) requestAttributes).getRequest();
        User user = userService.getLoginUser(request);
        // 拥有anyRole中任意权限即通过
        if (CollectionUtils.isNotEmpty(anyRole)) {
            String userRole = user.getUserRole();
            if (!anyRole.contains(userRole)) {
                throw new BusinessException(ErrorCode.NO_AUTH_ERROR);
            }
        }
        // 必须有所有权限才通过
        if (StringUtils.isNotBlank(mustRole)) {
            String userRole = user.getUserRole();
            if (!mustRole.equals(userRole)) {
                throw new BusinessException(ErrorCode.NO_AUTH_ERROR);
            }
        }
        // 通过权限校验,放行
        return joinPoint.proceed();
    }
}

数据库 user 表中,有一个 varChar 类型的 userRole 字段。

更完善的权限管理方法,应该使用 RBAC 模型,即数据库要有 权限表、角色表、用户表、角色权限表、用户角色表。而鱼皮这里只是简单使用一个字段表示用户角色。

完美的方案参考:

springboot+自定义注解+AOP实现权限控制(一)

springboot+自定义注解+AOP实现权限控制(二)

0

评论区