基于SpringAOP实现自定义接口权限控制

文章目录

  • 一、接口鉴权方案分析
    • 1、接口鉴权方案
    • 2、角色分配权限树
  • 二、编码实战
    • 1、定义权限树与常用方法
    • 2、自定义AOP注解
    • 3、AOP切面类(也可以用拦截器实现)
    • 4、测试一下

一、接口鉴权方案分析

1、接口鉴权方案

目前大部分接口鉴权方案,一般都是采用 【用户-角色-权限】模型。

将接口与权限进行编码死绑定,同时角色与权限进行动态绑定,用户与角色也进行动态绑定。

2、角色分配权限树

创建用户之后,用户可以分配多个角色。

创建角色之后,通过查询代码内置的权限树,进行角色与权限绑定,存入数据库中。
在这里插入图片描述

二、编码实战

1、定义权限树与常用方法

使用枚举进行权限的定义,通过四级权限树,将权限分为模块、单元、功能级、接口。接口权限精细分配:

/*** 接口权限枚举定义类*/
public enum AuthPermission {/*** 四级权限树* 1 模块* - 2 功能* - - 3 接口集(一般是Controller)* - - - 4 具体接口(@RequestMapping)**/// 用户管理User("user", "用户", Type.Module),SysUser(User, "系统用户", Type.Unit),SysUserManager(SysUser, "系统用户管理", Type.Bunch),SysUserManagerSelect(SysUserManager, "系统用户查询", Type.Function),SysUserManagerAdd(SysUserManager, "系统用户新增", Type.Function),SysUserManagerUpdate(SysUserManager, "系统用户修改", Type.Function),SysUserManagerDelete(SysUserManager, "系统用户删除", Type.Function),NormalUser(User, "普通用户", Type.Unit),NormalUserManager(NormalUser, "普通用户管理", Type.Bunch),NormalUserManagerSelect(NormalUserManager, "普通用户查询", Type.Function),NormalUserManagerAdd(NormalUserManager, "普通用户新增", Type.Function),NormalUserManagerUpdate(NormalUserManager, "普通用户修改", Type.Function),NormalUserManagerDelete(NormalUserManager, "普通用户删除", Type.Function),// 订单管理Order("order", "订单", Type.Module),OrderConfirm(Order, "下单管理", Type.Unit),OrderConfirmKill(OrderConfirm, "秒杀下单管理", Type.Bunch),OrderConfirmKillAdd(OrderConfirmKill, "秒杀订单新增", Type.Function),OrderConfirmKillDelete(OrderConfirmKill, "秒杀订单删除", Type.Function),OrderConfirmNormal(OrderConfirm, "普通下单管理", Type.Bunch),OrderConfirmNormalAdd(OrderConfirmNormal, "普通订单新增", Type.Function),OrderConfirmNormalDelete(OrderConfirmNormal, "普通订单删除", Type.Function),// ...其他;/*** 功能权限类型*/public enum Type {Module, // 功能模块Unit, // 功能单元Bunch, // 功能接口集Function, // 功能接口}// 模块private final String module;// 名称private final String title;// 父private final AuthPermission parent;// 类型private final Type type;AuthPermission(AuthPermission parent, String title, Type type) {this(parent.module, parent, title, type);}AuthPermission(String title, String module, Type type) {this(module, null, title, type);}AuthPermission(String module, AuthPermission parent, String title, Type type) {this.module = module;this.title = title;this.parent = parent;this.type = type;}public String getModule() {return module;}public String getTitle() {return title;}public AuthPermission getParent() {return parent;}public Type getType() {return type;}
}

import org.springframework.util.CollectionUtils;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;/*** 接口权限管理类*/
public class AuthPermissionManagement {private static List<AuthArchetype> permissionTree = new ArrayList<>();private static Map<AuthPermission, AuthArchetype> permissionMap = new HashMap<>();static {// 递归设置树// 设置子树recursePermissions(permissionTree, permissionMap, null);}public static void main(String[] args) {// 获取权限树(到时候给前端展示树)System.out.println(Json.toJson(getPermissionTree()));// 校验权限System.out.println(checkPermission(AuthPermission.NormalUserManagerDelete, AuthPermission.NormalUser));System.out.println(checkPermission(AuthPermission.NormalUserManagerDelete, AuthPermission.OrderConfirm));System.out.println(checkPermission(AuthPermission.NormalUserManagerDelete, AuthPermission.NormalUserManagerDelete));}/*** 校验权限 递归* @param userPermission 用户角色权限* @param interfacePermission 接口权限* @return*/public static boolean checkPermission(AuthPermission userPermission, AuthPermission interfacePermission) {if (userPermission == interfacePermission) {return true;}// 有子接口权限也可AuthArchetype authArchetype = permissionMap.get(interfacePermission);if (authArchetype == null) {return false;}return checkChildrenPermission(userPermission, authArchetype);}private static boolean checkChildrenPermission(AuthPermission userPermission, AuthArchetype authArchetype) {if (authArchetype.getName().equals(userPermission.name())) {return true;}if (!CollectionUtils.isEmpty(authArchetype.getSubPermissions())) {for (AuthArchetype subPermission : authArchetype.getSubPermissions()) {if (subPermission.getName().equals(userPermission.name())) {return true;}// 递归if (checkChildrenPermission(userPermission, subPermission)) {return true;}}}return false;}// 获取权限树public static List<AuthArchetype> getPermissionTree() {return permissionTree;}private static void recursePermissions(List<AuthArchetype> permissionTree, Map<AuthPermission, AuthArchetype> permissionMap, AuthPermission current) {for (AuthPermission permission : AuthPermission.values()) {if (permission.getParent() == current) {AuthArchetype permissionArchetype = new AuthArchetype(permission);if (current == null) {permissionTree.add(permissionArchetype);} else {permissionMap.get(current).addSubPermission(permissionArchetype);}permissionMap.put(permission, permissionArchetype);recursePermissions(permissionTree, permissionMap, permission);}}}public static class AuthArchetype {// nameprivate String name;// 模块private String module;// 名称private String title;// 父private AuthPermission parent;// 类型private AuthPermission.Type type;// 子private List<AuthArchetype> subPermissions;public AuthArchetype(AuthPermission permission) {this.name = permission.name();this.module = permission.getModule();this.title = permission.getTitle();this.parent = permission.getParent();this.type = permission.getType();}public void addSubPermission(AuthArchetype subPermission) {if (this.subPermissions == null) {this.subPermissions = new ArrayList<>();}this.subPermissions.add(subPermission);}public String getName() {return name;}public void setName(String name) {this.name = name;}public String getModule() {return module;}public void setModule(String module) {this.module = module;}public String getTitle() {return title;}public void setTitle(String title) {this.title = title;}public AuthPermission getParent() {return parent;}public void setParent(AuthPermission parent) {this.parent = parent;}public AuthPermission.Type getType() {return type;}public void setType(AuthPermission.Type type) {this.type = type;}public List<AuthArchetype> getSubPermissions() {return subPermissions;}public void setSubPermissions(List<AuthArchetype> subPermissions) {this.subPermissions = subPermissions;}}
}

2、自定义AOP注解

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface Auth {AuthPermission[] value() default {};
}

3、AOP切面类(也可以用拦截器实现)

import org.apache.commons.lang3.ArrayUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;import java.lang.reflect.Method;@Aspect
@Component
public class AuthAdvice {/*** 我们希望的是如果方法上有注解,则对方法进行限制,若方法上无注解,单是类上有注解,那么类上的权限注解对该类下所有的接口生效。*/@Around("@annotation(com.qstcloud.athena.opencourse.auth.Auth) || @within(com.qstcloud.athena.opencourse.auth.Auth)")public Object preAuth(ProceedingJoinPoint point) throws Throwable {if (handleAuth(point)) {return point.proceed();}throw new RuntimeException("权限不足,请求被拒绝");}/*** 逻辑判断,返回true or false* true :权限校验通过* false :权限校验不通过*/private boolean handleAuth(ProceedingJoinPoint point) {MethodSignature ms = point.getSignature() instanceof MethodSignature ? (MethodSignature) point.getSignature() : null;Method method = ms.getMethod();// 读取权限注解,优先方法上,没有则读取类Auth Annotation = getAnnotation(method, Auth.class);// 判断权限AuthPermission[] authPermissions = Annotation.value();if (ArrayUtils.isEmpty(authPermissions)) {// 没有权限树,登录就可以访问return checkLogin();}// 校验当前登录用户,是否包含其中权限之一return checkHasPermission(authPermissions);}/*** 校验当前登录用户,是否包含其中权限之一*/private boolean checkHasPermission(AuthPermission[] authPermissions) {
//        User user = UserUtil.getCurrentUser();
//        Role role = user.getRole(); // 获取角色// TODO 判断角色中的权限,是否包含接口的权限return ArrayUtils.contains(authPermissions, AuthPermission.NormalUserManagerUpdate);}/*** 判断是否登录*/private boolean checkLogin() {// TODO 从redis或者session中判断是否登录return true;}/*** 读取权限注解,优先方法上,没有则读取类*/private Auth getAnnotation(Method method, Class<Auth> authClass) {Auth annotation = method.getAnnotation(authClass);if (annotation != null) {return annotation;}annotation = method.getDeclaringClass().getAnnotation(authClass);return annotation;}
}

4、测试一下

@RestController
@RequestMapping("/test")
@Auth
public class TestController {@GetMapping("/test1")public String test1() {return "success";}@GetMapping("/test2")@Auth(AuthPermission.NormalUserManagerDelete)public String test2() {return "success";}@GetMapping("/test3")@Auth(AuthPermission.NormalUserManagerUpdate)public String test3() {return "success";}
}

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

中文sd:SkyPaint-AI-Diffusion

https://huggingface.co/SkyworkAIGC/SkyPainthttps://huggingface.co/SkyworkAIGC/SkyPainthttps://github.com/SkyWorkAIGC/SkyPaint-AI-Diffusionhttps://github.com/SkyWorkAIGC/SkyPaint-AI-Diffusion从model_index.json看&#xff0c;应该算是标准的sd1.5架构了。 {&quo…

【Head First 设计模式】-- 观察者模式

背景 客户有一个WeatherData对象&#xff0c;负责追踪温度、湿度和气压等数据。现在客户给我们提了个需求&#xff0c;让我们利用WeatherData对象取得数据&#xff0c;并更新三个布告板&#xff1a;目前状况、气象统计和天气预报。 WeatherData对象提供了4个接口&#xff1a; …

linux系统SQL server数据库定时收缩

问题现象 出现下图问题&#xff0c;导致连接该数据库的程序不能正常启动 解决办法 定时收缩数据库 数据库定时收缩脚本 需要三个脚本文件 linux_sqlcmd_timing_task_shrink.sh&#xff1a;主脚本文件 # 设置数据库名称、用户名、密码等信息 # db_name"volador"…

Elasticsearch:使用你的 RAG 来进行聊天

什么是人工智能中的检索增强生成&#xff08;RAG&#xff09;&#xff1f; 检索增强生成 (RAG)&#xff0c;与你的文档聊天的超级英雄&#xff0c;架起信息检索和文本生成世界的桥梁&#xff01; 这就像福尔摩斯和莎士比亚联手解决需要大量知识的复杂任务。 RAG 突然介入&…

使用Python自动修改电脑的静态IP地址

目录 一、引言 二、实现思路 三、详细步骤 四、Python代码 五、注意事项 六、适用性和局限性 七、总结 一、引言 在网络应用中&#xff0c;有时我们需要频繁更改电脑的静态IP地址。例如&#xff0c;当我们在不同网络环境&#xff08;家庭、办公室&#xff09;中使用电脑…

洗衣洗鞋柜洗衣洗鞋小程序

支持&#xff1a;一键投递、上门取衣、自主送店、多种支付方式 TEL: 17638103951(同V) -----------------用户下单-------------- -------------------------多种支付和投递方式------------------------- -----------------商家取鞋--------------

C++前缀和算法的应用:最大化城市的最小供电站数目

本文涉及的基础知识点 C算法&#xff1a;前缀和、前缀乘积、前缀异或的原理、源码及测试用例 包括课程视频 二分法 题目 给你一个下标从 0 开始长度为 n 的整数数组 stations &#xff0c;其中 stations[i] 表示第 i 座城市的供电站数目。 每个供电站可以在一定 范围 内给所…

算法题:203. 移除链表元素(递归法、设置虚拟头节点法等3种方法)Java实现创建链表与解析链表

1、算法思路 讲一下设置虚拟头节点的那个方法&#xff0c;设置一个新节点指向原来链表的头节点&#xff0c;这样我们就可以通过判断链表的当前节点的后继节点值是不是目标删除值&#xff0c;来判断是否删除这个后继节点了。如果不设置虚拟头节点&#xff0c;则需要将头节点和后…

产品经理墨刀学习----注册页面

我们做的产品是一个校园论坛学习开发系统&#xff0c;目前才开始学习。 &#xff08;一&#xff09;流程图 &#xff08;二&#xff09;简单墨刀设计--注册页面 &#xff08;1&#xff09;有账号 &#xff08;a&#xff09;直接登录&#xff1a; &#xff08;b&#xff09;忘…

在Google Kubernetes集群创建分布式Jenkins(一)

因为项目需要&#xff0c;在GKE的集群上需要创建一个CICD的环境&#xff0c;记录一下安装部署一个分布式Jenkins集群的过程。 分布式Jenkins由一个主服务器和多个Agent组成&#xff0c;Agent可以执行主服务器分派的任务。如下图所示&#xff1a; 如上图&#xff0c;Jenkins Ag…

从零开始搭建微服务(一)

构建项目父工程 添加公共核心模块 安装nacos 安装nacos nacos 文档地址&#xff1a; https://nacos.io/zh-cn/docs/what-is-nacos.html 本文使用版本2.2.2 下载地址&#xff1a;https://github.com/alibaba/nacos/archive/refs/tags/2.2.2.zip 使用nacos 我们下载是源代码 解…

20.8 OpenSSL 套接字SSL传输文件

有了上面的基础那么传输文件的实现就变得简单了&#xff0c;在传输时通常我们需要打开文件&#xff0c;并每次读入1024个字节的数据包&#xff0c;通过SSL加密传输即可&#xff0c;此处的文件传输功能在原生套接字章节中也进行过详细讲解&#xff0c;此处我们还是使用原来的密钥…

【Verilog 教程】7.3 Verilog 串行 FIR 滤波器设计

串行 FIR 滤波器设计 设计说明 设计参数不变&#xff0c;与并行 FIR 滤波器参数一致。即&#xff0c;输入频率为 7.5 MHz 和 250 KHz 的正弦波混合信号&#xff0c;经过 FIR 滤波器后&#xff0c;高频信号 7.5MHz 被滤除&#xff0c;只保留 250KMHz 的信号。 输入频率&#x…

VMware安装CentOS最小化开发环境导引

目录 一、概要 二、介绍 三、下载 四、安装 4.1 创建虚拟机 4.2 安装CentOS 五、配置网卡 六、配置本地安装源 七、安装软件 7.1 gcc/g 7.2 C的atomic库 7.3 java 7.4 Cmake 7.5 MariaDB客户端&#xff08;兼容mysql&#xff09; 八、用户配置文件.bash_profile…

[开源]企业级在线办公系统,基于实时音视频完成在线视频会议功能

一、开源项目简介 企业级在线办公系统 本项目使用了SpringBootMybatisSpringMVC框架&#xff0c;技术功能点应用了WebSocket、Redis、Activiti7工作流引擎&#xff0c; 基于TRTC腾讯实时音视频完成在线视频会议功能。 二、开源协议 使用GPL-3.0开源协议 三、界面展示 部分…

什么是单片机?它是如何工作的?

一.单片机是什么&#xff1f; 家用电器包含各种各样的集成电路板。 在集成电路板上最重要的就是单片机&#xff08;单片微型计算机&#xff09;。它是一种集成电路芯片。 二.单片机的组成 2.1 CPU CPU的主要功能是运算和控制。2.2 ROM 1.是什么&#xff1f; ROM的全称是Re…

Redis03-过期策略和淘汰策略

目录 Redis数据过期策略 Redis数据淘汰策略 Redis数据过期策略 Redis使用一种基于过期策略来处理键的过期和自动失效。这种策略可以确保不再需要的数据被自动删除&#xff0c;以释放内存并避免数据过期后仍然在缓存中存留。 Redis的过期删除策略主要有两种&#xff1a; 惰性…

【SpringSecuirty6.x】自动登录和注销登录

关于网站的安全设计,通常是有一些矛盾点的。我们在作为某些系统开发者的同时,也在充当着另外一些系统的用户,一些感同身受的东西可以带来很多思考。 5.1、为什么需要自动登录 当我们在某个网站上注册账号时,网站会对我们设置的登录密码提出要求。例如,有的网站要求使用固…

VS编译器中创建新源文件,自动添加----#define _CRT_SECURE_NO_WARNINGS 1

每次创建新的源文件时都需要添加比较麻烦&#xff0c;我们让每次创建时自动添加&#xff0c;步骤如下&#xff1a; 1、右键编译器--->属性--->目标&#xff08;存放的是编译器的文件位置我们找到文件的位置&#xff09; 2、如下步骤找到“newc file.cpp”文件&#xff0…

如何使用Ruby 多线程爬取数据

现在比较主流的爬虫应该是用python&#xff0c;之前也写了很多关于python的文章。今天在这里我们主要说说ruby。我觉得ruby也是ok的&#xff0c;我试试看写了一个爬虫的小程序&#xff0c;并作出相应的解析。 Ruby中实现网页抓取&#xff0c;一般用的是mechanize&#xff0c;使…