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

一,前言

对于一些重复性的操作我们可以用提取为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;
    }
}

四,接口测试

当正确有权限的时候

当没有权限的时候的返回

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/715080.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Centos7离线安装GCC,G++

系统:Centos7,Py版本:3.9.0 解压完python包后,执行./configure --prefix/usr/local/python39 --enable-shared编译时显示缺少相关编译器,即缺少gcc相关的C编译器,内容如下: 安装gcc所需要的依…

【算法专题--链表】删除排序链表中的重复元素II -- 高频面试题(图文详解,小白一看就懂!!)

目录 一、前言 二、题目描述 三、解题方法 ⭐ 双指针 -- 采用 哨兵位头节点 🥝 什么是哨兵位头节点? 🍍 解题思路 🍍 案例图解 四、总结与提炼 五、共勉 一、前言 删除排序链表中的重复元素II元素这道题&#xff0c…

嵌入式复古游戏项目开发与实现

大家好,今天看到一个火柴盒项目,非常的小巧,分享给大家,感兴趣的话,可以复刻一个玩一玩。 MicroByte 是一款微型主机,能够运行 NES、GameBoy、GameBoy Color、Game Gear 和 Sega Master 系统的游戏,所有元器件都设计在这 78 x 17 x 40 mm 的封装中。尽管成品尺寸很小,但…

什么是git?

前言 Git 是一款免费、开源的分布式版本控制系统,用于敏捷高效地处理任何或小或大的项目。是的,我对git的介绍就一条,想看简介的可以去百度一下😘😘😘 为什么要用git? OK,想象一下…

【单片机毕业设计选题24008】-基于单片机的寝室系统设计

系统功能: 1. 采用STM32最小系统板控制,将采集到温湿度光照等传感器数据显示在OLED上 2. 通过离线语音模块开关灯,风扇,门。 3. 监测到MQ2烟雾后触发报警。 4. 语音&手动&定时控制窗帘。 5. 按键开启布防模式,布防后…

C语言实现动态栈

#include<stdio.h> #include<stdlib.h> #include<stdbool.h>// 每一个节点的数据类型 typedef struct Node {int data;struct Node * pNext; }NODE, * PNODE; // NODE等价 struct Node PNODE等价于 struct Node *// 栈 typedef struct Stack {PNODE pTop;P…

Modbus为何要转成ProfiNET

Modbus与ProfiNET代表了工业通讯不同阶段的发展&#xff0c;各自具有优缺点。Modbus简单易用&#xff0c;适合小型系统&#xff1b;ProfiNET高效稳定&#xff0c;适用于大型复杂网络。转换Modbus为ProfiNET可提高系统性能和扩展性。实际场景下&#xff0c;升级生产线控制器为Pr…

Golang: 依赖注入与wire —— 构建高效模块化应用的秘诀

&#x1f49d;&#x1f49d;&#x1f49d;欢迎莅临我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:「stormsha的主页」…

代码随想录-Day32

122. 买卖股票的最佳时机 II 给你一个整数数组 prices &#xff0c;其中 prices[i] 表示某支股票第 i 天的价格。 在每一天&#xff0c;你可以决定是否购买和/或出售股票。你在任何时候 最多 只能持有 一股 股票。你也可以先购买&#xff0c;然后在 同一天 出售。 返回 你能…

Python文本处理:初探《三国演义》

Python文本处理&#xff1a;初探《三国演义》 三国演义获取文本文本预处理分词与词频统计引入停用词后进行词频统计分析人物出场次数结果可视化完整代码 三国演义 《三国演义》是中国古代四大名著之一&#xff0c;它以东汉末年到晋朝统一之间的历史为背景&#xff0c;讲述了魏…

阿里云服务器-Linux搭建fastDFS文件服务器

阿里云官网购买服务器&#xff0c;一般会有降价活动&#xff0c;这两天就发现有活动&#xff0c;99计划活动&#xff08;在活动期内&#xff0c;续费都是99元&#xff09; 阿里云官网-云服务器ECS 在这里&#xff0c;我购买了这台服务器&#xff0c;活动期内续费每年99元&…

二叉树-距离是K的二叉树节点(hard)

目录 一、问题描述 二、解题思路 1.总体思路&#xff08;DFSBFS结合&#xff09; 2.下面举具体例子来对思路进行解释 (1)返回值在一侧的情况 (2)返回值在两侧的情况 三、代码实现 四、刷题链接 一、问题描述 二、解题思路 1.总体思路&#xff08;DFSBFS结合&#xff0…

对接钉钉Stream模式考勤打卡相关事件的指南

钉钉之前的accessToken是公司级别的&#xff0c;现在的accessToken是基于应用的&#xff0c;接口的权限也是基于应用的。所以第一步是在钉钉开放平台&#xff08;https://open-dev.dingtalk.com/&#xff09;创建一个应用。 创建好应用之后&#xff0c;因为我们后续还需要调用钉…

---异常---

我们在运行程序时总遇到各种与报错&#xff0c;数组越界&#xff0c;空指针的引用&#xff0c;这些在java中都称为异常 对于不同的错误都具有一个与他对应的异常类来秒描述 这是对于数组越界这个类里有的方法&#xff0c;这些是描述异常的 在java中有一个完整的描述异常的类的…

C/C++ Adaline自适应线性神经网络算法详解及源码

个人名片 &#x1f393;作者简介&#xff1a;java领域优质创作者 &#x1f310;个人主页&#xff1a;码农阿豪 &#x1f4de;工作室&#xff1a;新空间代码工作室&#xff08;提供各种软件服务&#xff09; &#x1f48c;个人邮箱&#xff1a;[2435024119qq.com] &#x1f4f1…

MySQL之高级特性(四)

高级特性 查询缓存 什么情况下查询缓存能发挥作用 并不是什么情况下查询缓存都会提高系统性能的。缓存和失效都会带来额外的消耗&#xff0c;所以只有当缓存带来的资源节约大于本身的资源消耗时才会给系统带来性能提升。这跟具体的服务器压力模型有关。理论上&#xff0c;可…

实现贪吃蛇小游戏【简单版】

1. 贪吃蛇游戏设计与分析 1.1 地图 我们最终的贪吃蛇大纲要是这个样子&#xff0c;那我们的地图如何布置呢&#xff1f; 这里不得不讲⼀下控制台窗口的⼀些知识&#xff0c;如果想在控制台的窗口中指定位置输出信息&#xff0c;我们得知道该位置的坐标&#xff0c;所以首先介…

微信小程序毕业设计-博客系统项目开发实战(附源码+论文)

大家好&#xff01;我是程序猿老A&#xff0c;感谢您阅读本文&#xff0c;欢迎一键三连哦。 &#x1f49e;当前专栏&#xff1a;微信小程序毕业设计 精彩专栏推荐&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb; &#x1f380; Python毕业设计…

龙迅LT9611UXC 2 PORT MIPIDSI/CSI转HDMI 2.1,支持音频IIS/SPDIF输入,支持标准4K60HZ输出

龙迅LT9611UXC描述&#xff1a; LT9611UXC是一个高性能的MIPI DSI/CSI到HDMI2.0转换器。MIPI DSI/CSI输入具有可配置的单端口或双端口&#xff0c;1高速时钟通道和1~4高速数据通道&#xff0c;最大2Gbps/通道&#xff0c;可支持高达16Gbps的总带宽。LT9611UXC支持突发模式DSI视…