使用自定义注解进行权限校验

一,前言

对于一些重复性的操作我们可以用提取为util的方式进行处理,但也可以更简便一些,比如自定义个注解进行。选择看这篇文章的小伙伴想必都对注解不陌生,但是可能对它的工作原理不太清楚。这里我们用注解实现对接口的权限校验,让大家感受一下,自定义注解的魅力。

二, 注解的基本认识

1,作用域

我们知道,注解可以用在类上,方法上,参数上等等。这也决定了注解的作用域可以是多方面的。

主要有三类:

  • ElementType.TYPE:表示该注解可以用于类、接口(包括注解类型)或枚举声明。例如,常见的注解 @Entity@Service 就是使用在类上的注解。

  • ElementType.FIELD:表示该注解可以用于字段(包括枚举常量)。例如,常见的注解 @Autowired 就是使用在字段上的注解。

  • ElementType.METHOD:表示该注解可以用于方法声明。例如,常见的注解 @Override 就是使用在方法上的注解。

还有其它作用域。注解的作用域由 java.lang.annotation.ElementType 枚举类型定义。我们来看看有哪些吧。

public enum ElementType {
    /** Class, interface (including annotation interface), enum, or record
     * declaration */
    // 可以用于类、接口、枚举声明。
    TYPE,

    /** Field declaration (includes enum constants) */
    // 可以用于字段(包括枚举常量)。
    FIELD,

    /** Method declaration */
    // 可以用于方法声明。
    METHOD,

    /** Formal parameter declaration */
    // 可以用于参数声明。
    PARAMETER,

    /** Constructor declaration */
    // 可以用于构造函数声明。
    CONSTRUCTOR,

    /** Local variable declaration */
    // 可以用于局部变量声明。
    LOCAL_VARIABLE,

    /** Annotation interface declaration (Formerly known as an annotation type.) */
    // 可以用于注解类型声明(即声明注解的注解)。
    ANNOTATION_TYPE,

    /** Package declaration */
    // 可以用于包声明。
    PACKAGE,

    /**
     * Type parameter declaration
     *
     * @since 1.8
     */
    // 可以用于泛型类型参数的声明。
    TYPE_PARAMETER,

    /**
     * Use of a type
     *
     * @since 1.8
     */
    // 可以用于任何类型使用的地方,如类型转换、instanceof 表达式、new 表达式等。
    TYPE_USE,

    /**
     * Module declaration.
     *
     * @since 9
     */
    // 可以用于模块声明,即 module-info.java 文件中的 module 关键字所声明的模块。该注解用于对模块进行注解,例如指定模块的名称、版本等信息。
    MODULE,

    /**
     * Record component
     *
     * @jls 8.10.3 Record Members
     * @jls 9.7.4 Where Annotations May Appear
     *
     * @since 16
     */
    // (Record)类型,它是一种简化的数据持有类,用于替代传统的 Java Bean 类。而 RECORD_COMPONENT 注解用于标记记录类型中的组件,也就是记录的字段或者对应的 accessor 方法。
    RECORD_COMPONENT;
}

2,生命周期

注解还有一个重要的属性,就是生命周期。

注解的生命周期由 java.lang.annotation.RetentionPolicy 枚举类型定义,它规定了注解在编译时、类加载时和运行时的保留策略。

public enum RetentionPolicy {
    /**
     * Annotations are to be discarded by the compiler.
     */
    SOURCE,

    /**
     * Annotations are to be recorded in the class file by the compiler
     * but need not be retained by the VM at run time.  This is the default
     * behavior.
     */
    CLASS,

    /**
     * Annotations are to be recorded in the class file by the compiler and
     * retained by the VM at run time, so they may be read reflectively.
     *
     * @see java.lang.reflect.AnnotatedElement
     */
    RUNTIME
}
  • SOURCE:该注解只在源代码中保留,编译器在编译时会丢弃这种注解,不会包含在编译后生成的 class 文件中。这意味着在运行时无法通过反射获取到这种注解。

  • CLASS:该注解在编译时会被保留到 class 文件中,但在运行时不会被虚拟机保留,因此通过反射也无法获取到。这是默认的保留策略。

  • RUNTIME:该注解在编译时会被保留到 class 文件中,并且在运行时会被虚拟机保留,因此可以通过反射机制获取到。这种注解通常用于运行时的操作,比如使用反射机制检查类的结构或者处理注解信息。

三,实践使用

1,启动类

package com.luojie;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;


@SpringBootApplication
public class Applications {

    public static void main(String[] args) {
        SpringApplication.run(Applications.class, args);
    }
}

2, 注解类

package com.luojie.config.myInterface;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface MyPermission {
    String value() default "";
}

3,aop实现校验

这里请自己往数据库植入数据,并做好查询返回,如果有不知的地方,可以参照

Spring配置多数据库(采用数据连接池管理)_spring连接多个数据库-CSDN博客

package com.luojie.config.myInterface;

import com.luojie.common.NoPermissionException;
import com.luojie.dao.mapper1.Mapper1;
import com.luojie.moudle.UserModel;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;

@Component
@Aspect
public class PermissionAspect {

    @Autowired
    private HttpServletRequest request;

    @Autowired
    private Mapper1 mapper1;

    @Before("@annotation(requiresPermission)")
    public void checkPermission(MyPermission requiresPermission) {
        String permission = requiresPermission.value();
        // 在这里进行权限校验逻辑
        // 检查用户是否拥有指定的权限,如果没有权限,可以抛出异常或者记录日志等
        if (!hasPermission(permission)) {
            throw new NoPermissionException(500, "没有权限");
        }
    }

    private boolean hasPermission(String permission) {
        // 一般我们会通过request拿token,解析token和数据库中数据比对,看用户是否有权限,这里我就简化为直接的值
        String userID = request.getHeader("userID");
        // 从数据库中拿到该用户的所有权限
        UserModel user = mapper1.getUser(userID);
        // 进行权限判断
        if (user == null) return false;
        if (user.getRoles().contains(permission)) {
            return true;
        }
        return false;
    }

}

4,统一异常处理类

package com.luojie.common;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.servlet.http.HttpServletResponse;


@ControllerAdvice
@ResponseBody
public class CommonExceptionHandle {

    @Autowired
    private HttpServletResponse response;

    @ExceptionHandler(NoPermissionException.class)
    public ResponseCommonImpl NoPermission(NoPermissionException ex) {
        ResponseCommonImpl failedCommon = ResponseUtil.failWithNoPermission(ex.getErrorMsg());
        response.setStatus(HttpStatus.UNAUTHORIZED.value());
        return failedCommon;
    }
    
    @ExceptionHandler(Exception.class)
    public ResponseCommonImpl handleException(Exception ex) {
        ResponseCommonImpl failedCommon = ResponseUtil.failCommon(ex.getMessage(), null);
        return failedCommon;
    }
}

5,返回类

统一返回类写法,请参照JAVA 标准接口返回与i18n国际化配置_java 后端接口返回支持国际化-CSDN博客

6,controller类

package com.luojie.controller;

import com.luojie.common.ResponseCommonImpl;
import com.luojie.common.ResponseUtil;
import com.luojie.controImpl.InterfaceTestImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class InterfaceTestController {

    @Autowired
    private InterfaceTestImpl interfaceTest;

    @GetMapping("/in/test")
    public ResponseCommonImpl test() {
        System.out.println("111111111111111222222222");
        interfaceTest.test();
        return ResponseUtil.success("ok", null);
    }
}

7,编写Impl类

package com.luojie.controImpl;

import com.luojie.common.ResponseCommonImpl;
import com.luojie.config.myInterface.MyPermission;
import org.springframework.stereotype.Service;

@Service
public class InterfaceTestImpl {

    @MyPermission("admin")
    public ResponseCommonImpl test() {
        System.out.println("ok!");
        return null;
    }
}

四,接口测试

当正确有权限的时候

当没有权限的时候的返回

如果对您有用,感谢老爷点个赞,谢谢。

相关推荐

  1. Spring Boot编写定义校验注解

    2024-06-17 10:18:02       40 阅读
  2. 定义注解实现对实体类的字段进行校验

    2024-06-17 10:18:02       16 阅读
  3. Spring Boot 中定义中文校验注解的实现

    2024-06-17 10:18:02       34 阅读
  4. SpringBoot项目使用JWT令牌进行权限校验

    2024-06-17 10:18:02       31 阅读

最近更新

  1. TCP协议是安全的吗?

    2024-06-17 10:18:02       18 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-06-17 10:18:02       19 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-06-17 10:18:02       18 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-06-17 10:18:02       20 阅读

热门阅读

  1. 程序员做电子书产品变现的复盘(4)

    2024-06-17 10:18:02       7 阅读
  2. 5.3.1_3 由遍历序列构造二叉树

    2024-06-17 10:18:02       6 阅读
  3. tapir_速读

    2024-06-17 10:18:02       6 阅读
  4. CRC8校验算法源码——C语言版

    2024-06-17 10:18:02       8 阅读
  5. cmake target_link_libraries 详解

    2024-06-17 10:18:02       10 阅读
  6. metaRTC8.0,一个全新架构的webRTC SDK库

    2024-06-17 10:18:02       6 阅读
  7. 深拷贝和浅拷贝

    2024-06-17 10:18:02       10 阅读