基于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.hqwc.cn/news/162055.html

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

相关文章

SurfaceFlinger的硬件Vsync深入分析-千里马android framework车机手机系统开发

背景&#xff1a; 学过或者你看过surfaceflinger相关文章同学都知道&#xff0c;vsync其实都是由surfaceflinger软件层面进行模拟的&#xff0c;但是软件模拟有可能会有误差或偏差&#xff0c;这个时候就需要有个硬件vsync帮忙校准。 故才会在surfaceflinger的systrace出现如下…

应用安全四十二:SSO安全

一、什么是SSO SSO是单点登录(Single Sign On)的缩写,是指在多个应用系统中,用户只需要登录一次就可以访问所有相互信任的应用系统。这种方式减少了由登录产生的时间消耗,辅助了用户管理,是比较流行的企业业务整合的解决方案之一。 身份验证过程依赖于双方之间的信任关…

新一代存储介质技术SCM探讨

什么是SCM介质&#xff1f; SCM介质现状 SCM介质产品形态和在存储系统中的应用 高性能SSD 字节型DIMM形态 小结 什么是SCM介质&#xff1f; SCM&#xff08;Storage Class Memory&#xff09;是当前业界非常热门的新介质形态&#xff0c;同时具备持久化&#xff08;Storage Cla…

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…

python学习10

前言&#xff1a;相信看到这篇文章的小伙伴都或多或少有一些编程基础&#xff0c;懂得一些linux的基本命令了吧&#xff0c;本篇文章将带领大家服务器如何部署一个使用django框架开发的一个网站进行云服务器端的部署。 文章使用到的的工具 Python&#xff1a;一种编程语言&…

MySQL进阶之性能优化与调优技巧

数据库开发-MySQL 1. 多表查询1.1 概述1.1.2 介绍1.1.3 分类 1.2 内连接1.3 外连接1.4 子查询1.4.1 介绍1.4.2 标量子查询1.4.3 列子查询1.4.4 行子查询1.4.5 表子查询 2. 事务2.1 介绍2.2 操作2.3 四大特性 3. 索引3.1 介绍3.2 结构3.3 语法 1. 多表查询 1.1 概述 1.1.2 介绍…

红黑树——原理刨析

众所周知&#xff0c;红黑树是从AVLTree树中衍变而来的&#xff0c;所以在学红黑树之前还是要好好的理解一下AVLTree树的原理&#xff0c;为理解红黑树减轻理解负担&#xff0c;好了进入正题。 红黑树原理&#xff1a; 由名可知&#xff0c;红黑树——肯定是与颜色有关的一个树…

gcc -static 在centos stream8 和centos stream9中运行报错的解决办法

gcc -static 在centos stream8 和centos stream9中运行报错的解决办法&#xff1a; 报/usr/bin/ld: cannot find -lc 我们下载glibc-static&#xff1a; 选择x86_64的。 还有一个是libxcrypt-static&#xff0c;依旧在这个网站里搜。 rpm -ivh glibc-static-2.28-239.el8.x…

CCF-CSP真题《202309-2 坐标变换(其二)》思路+python,c++满分题解

想查看其他题的真题及题解的同学可以前往查看&#xff1a;CCF-CSP真题附题解大全 试题编号&#xff1a;202309-2试题名称&#xff1a;坐标变换&#xff08;其二&#xff09;时间限制&#xff1a;2.0s内存限制&#xff1a;512.0MB问题描述&#xff1a; 问题描述 对于平面直角坐标…

【CIO人物展】国家能源集团信息技术主管王爱军:中国企业数智化转型升级的内在驱动力...

王爱军 本文由国家能源集团信息技术主管王爱军投递并参与《2023中国数智化转型升级优秀CIO》榜单/奖项评选。丨推荐企业—锐捷网络 大数据产业创新服务媒体 ——聚焦数据 改变商业 随着全球信息化和网络化的进程日益加速&#xff0c;数字化转型已经成为当下各大企业追求的核心…

Scala语言用Selenium库写一个爬虫模版

首先&#xff0c;我将使用Scala编写一个使用Selenium库下载yuanfudao内容的下载器程序。 然后我们需要在项目的build.sbt文件中添加selenium的依赖项。以下是添加Selenium依赖项的代码&#xff1a; libraryDependencies "org.openqa.selenium" % "selenium-ja…

✔ ★【备战实习(面经+项目+算法)】 11.5学习

✔ ★【备战实习&#xff08;面经项目算法&#xff09;】 坚持完成每天必做如何找到好工作1. 科学的学习方法&#xff08;专注&#xff01;效率&#xff01;记忆&#xff01;心流&#xff01;&#xff09;2. 每天认真完成必做项&#xff0c;踏实学习技术 认真完成每天必做&…