springboot通用分支处理超级管理员权限逻辑

目录
  • 前言
  • 自定义注解
    • SuperDirection
    • SuperDirectionHandler
    • 作用
    • 位置
  • 切面
  • 条件判断
  • 表达式解析
  • 演示使用
    • controller
    • service
    • 测试
  • 总结

前言

当引入登录模块后我们需要做菜单。而菜单自然需要权限的参与,我们在springboot中设计的权限细粒度还算是比较细的。当我们查询菜单是需要根据权限查找对应的菜单。但是在springboot中我设计了一个底层超级管理员

  • 先来看看我一开始实现这个超级管理员菜单获取的部分代码
 if (SecurityUtils.getSubject().hasRole(RoleList.SUPERADMIN)) {
     listemp = customMapper.selectRootMenusByRoleIdList(null,null, null, null);
 } else {
     listemp = customMapper.selectRootMenusByRoleIdList(roleList, oauthClientId,null, moduleCodes);
 }

这样实现是很正常的思路,通过判断角色是否是超级管理员来做分支执行思路,但是超级管理员可能涉及到多个地方如果在每个地方都这样if else执行的,我觉得有点low, 所以我决定改造一下。不够最终执行的思路依然是if else判断 。 只不过让我们在代码层面上功能间不在那么杂糅在一起

自定义注解

首先我需要两个注解,SuperDirectionSuperDirectionHandler分别表示需要判断超级管理员分支和具体管理员分支的目标函数 。 这句话说的还是有点抽象的,容我慢慢道来!

SuperDirection

 @Target({ElementType.TYPE, ElementType.METHOD})
 @Retention(RetentionPolicy.RUNTIME)
 @Documented
 public @interface SuperDirection {
     String value() default StringUtils.EMPTY;
 }

SuperDirectionHandler

 @Target({ElementType.TYPE, ElementType.METHOD})
 @Retention(RetentionPolicy.RUNTIME)
 @Documented
 @Component
 public @interface SuperDirectionHandler {
 }

作用

SuperDirection是用于表明该方法需要进行判断超级管理员,而value值存储的就是判断的表达式。关于这个表达式我们后面介绍

SuperDirectionHandler我们不难发现他没有实际属性但是多了一个@Component注解。目的是方便Spring管理该注解;这样我们就可以通过Spring来获取被该注解标注的类了。

位置

该注解释放给全局使用的,在maltcloud结构介绍中我们知道org.framework.core模块是所有模块的基石,所以这两个注解我选择在org.framework.core模块中

切面

  • 我们想在方法执行前进行条件判断,可选方案有很多我们可以在filter中拦截方法进行判断选择执行哪一个,但是过滤器中我们无法直接获取到方法的相关信息,注意这里说的是无法直接获取,如果你想在filter中实现也不是不行,这种方案感兴趣的可以试试
  • spring的另外一个特性切面正好符合我们的需求,我们只需要在aroud环绕方法中实现我们的需要。
  • 首先我们定义一个切点,切点拦截所有被SuperDirection注解标注的类或者方法。
 /**定义一个切点; 拦截所有带有SuperDirection注解的类和方法*/
 @Pointcut("@annotation(com.github.zxhtom.core.annotaion.SuperDirection) || @within(com.github.zxhtom.core.annotaion.SuperDirection)")
 public void direction() {
 }
  • 这里稍作一下解释 @annotation用于标识方法上的SuperDirection@within用于标识在类上的SuperDirection
  • 正常我们的一个业务处理都是放在service层的,spring中的三层架构的service正常是实现一个接口然后实现。所以我们这里在切面中先获取被拦截对象实现的接口。获取到接口信息我们通过接口信息获取该接口在spring中的其它实现类
  • 在spring容器中提供了获取bean集合的方法,加上我们maltcloud中实现了获取ApplicationContext的工具类,所以我们通过如下来获取bean集合
 Map<String, ?> beansOfType = ApplicationContextUtil.getApplicationContext().getBeansOfType(接口class);
  • 获取到集合了,此时我们还是无法确定哪一个实现类使我们替补执行超级管理员的bean 。 这就需要我们SuperDirectionHandler注解了。
  • 这个时候我们在通过Spring获取被该注解标识的类。这个时候获取到很多不想关类,我们在和上面的beansOfType进行比对。就可以确定哪一个实现bean是我们需要的。
 @Around("direction()")
 public Object aroud(ProceedingJoinPoint pjp) throws Throwable {
     Class<?>[] inters = pjp.getTarget().getClass().getInterfaces();
     for (Class<?> inter : inters) {
         Map<String, ?> beansOfType = ApplicationContextUtil.getApplicationContext().getBeansOfType(inter);
         Map<String, Object> beansWithAnnotation = ApplicationContextUtil.getApplicationContext().getBeansWithAnnotation(SuperDirectionHandler.class);
         for (Map.Entry<String, ?> entry : beansOfType.entrySet()) {
             if (beansWithAnnotation.containsKey(entry.getKey())) {
                 try {
                     return doOthersHandler(entry.getValue(), pjp);
                 } catch (Exception e) {
                     log.error("分支执行失败,系统判定执行原有分支....");
                 }
             }
         }
     }
     return pjp.proceed();
 }
  • 当确定执行类之后,我们只需要携带着该bean ,在根据SuperDirection上的表达式进行判断是执行超级管理员实现类还是原有实现类的方法了。

条件判断

  • 在Aspect执行中原有方法的执行很简单,只需要pjp.proceed()就可以了。所以这里我们先获取一下上面获取到的超级管理员实现bean的对应方法吧。
 MethodSignature msig = (MethodSignature) pjp.getSignature();
 Method targetMethod = value.getClass().getDeclaredMethod(msig.getName(),msig.getParameterTypes());
  • 然后我们在获取SuperDirection注解信息,因为该注解可能在类上,也可能在方法上,所以这里我们需要处理下
 SuperDirection superDirection = null;
 superDirection = targetMethod.getAnnotation(SuperDirection.class);
 if (superDirection == null) {
     superDirection = pjp.getTarget().getClass().getAnnotation(SuperDirection.class);
 }
  • 最终我们通过注解表达式判断执行情况
 if(selectAnnotationChoiceDo(superDirection)){
     //如果表达式验证通过,则执行替补bean实现类
     return targetMethod.invoke(value,pjp.getArgs());
 }
 //否则执行原有bean实现类
 return pjp.proceed();

表达式解析

  • 表达式解析涉及两个模块,一个是登录模块中获取当前登录用户的角色,另外一个是我们上面提到的表达式解析
  • 获取当前登录用户信息类我在org.framework.web中提供了bean 。 具体的实现由各个登录子模块负责去实现,这里我们只需要引入spirng bean使用就要可以了。这里充分体现了模块拆分的好处了。
  • 至于表达式解析,我选择放在本模块中org.framework.commons 。 这里目前简单提供了几个表达式解析。
 public interface RootChoiceExpression {
     public boolean haslogined();
     public boolean hasRole(String role);
     public boolean hasAnyRole(String... roles);
 }
  • 他们分别是验证是否登录、是否拥有角色和角色组。关于他的视线最终也还是依赖上面提到的org.framework.web模块中的登录用户的信息类
 @Service
 public class DefaultChoiceExpression implements RootChoiceExpression {
     @Autowired
     OnlineSecurity onlineSecurity;
     @Override
     public boolean haslogined() {
         return onlineSecurity.getOnlinePrincipal()!=null;
     }
     @Override
     public boolean hasRole(String role) {
         return onlineSecurity.hasAnyRole(role);
     }
     @Override
     public boolean hasAnyRole(String... roles) {
         return onlineSecurity.hasAnyRole(roles);
     }
 }
  • 这里也算是流出扩展吧,后面根据项目需求我们可以重写该表达式解析,在根据自己的业务进行表达式新增。这里仅作为基础功能
 private boolean selectAnnotationChoiceDo(SuperDirection superDirection) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
     String value = superDirection.value();
     if (StringUtils.isEmpty(value)) {
         return onlineSecurity.getRoleNames().contains(MaltcloudConstant.SUPERADMIN);
     }
     MethodInfo info = selectInfoFromExpression(value);
     Method declaredMethod = expression.getClass().getDeclaredMethod(info.getMethodName(), String.class);
     Object invoke = declaredMethod.invoke(expression, info.getArgs());
     if (invoke != null && invoke.toString().equals("true")) {
         return true;
     }
     return false;
 }
  • 首先根据正则解析出方法名和参数。然后根据反射调用我们spring中表达式bean去执行我们在SuperDirection配置的表达式。通过上面我们又能发现目前表达式仅支持String传参。因为在SuperDireciton传递过来的已经是String了,所以在这里目前我还没想到如何支持更多类型的数据。先埋坑吧!
  • 该方法最终决定执行原生方法还是替补方法。

演示使用

  • 上面说的那么枯燥主要是因为是我的一个设计思路,下面我们来实操感受一下吧。

controller

  • 首先我在controller中开发一个接口 。这里需要注意下因为我们上面会出现多个实现bean在spring中,所以我们在使用这些接口的时候就不能单纯的使用@Autowired了, 而需要通过beanName来使用了。这里名叫commonTestServiceImplCommonTestService接口的普通实现类,用于实现我们正常的操作。
 @RestController
 @RequestMapping(value = "/demo/common")
 public class CommonController {
     @Qualifier(value = "commonTestServiceImpl")
     @Autowired
     CommonTestService commonTestService;
     @RequestMapping(value = "/test",method = RequestMethod.GET)
     public void test() {
         commonTestService.test();
     }
 }

service

  • 这里有两个实现类分别是CommonTestServiceImplCommonTest2ServiceImpl
 @Service
 @SuperDirectionHandler
 public class CommonTest2ServiceImpl implements CommonTestService {
     @Override
     public void test() {
         System.out.println("hello test 2");
     }
 }
 @Service
 @SuperDirection(value = "")
 public class CommonTestServiceImpl implements CommonTestService {
     @Override
     public void test() {
         System.out.println("hello i am test ing ...");
     }
 }
  • 在controller层我们使用的是CommonTestServiceImpl用来实现正常的逻辑。而CommonTest2ServiceImpl是针对超级管理员做的操作。我们就可以进行如上的配置。在SuperDirection中配置空值标识判断超级管理员进行分支执行。你也可以配置目前支持的表达式,我这里简单点了。
  • 然后通过SuperDirectionHandler标识ConmmonTest2ServiceImpl是替补执行。

测试

  • 由于为了简单测试,我在org.framework.demo.common模块中还没有引入login模块,所以此时登录用户获取类还是我们默认的OnlineSecurityImpl

  • 通过上面代码我们可以看出来我们当前是没有角色的,所以我们可以理解成调用接口是没有超级管理员接口。那么就会执行我们CommonTestServiceImpl中的方法。然后我们在将这里的hasAnyRole改成true , 会发现就会执行CommonTest2ServiceImpl里的方法。

总结

  • 经过上面这么折腾,我们就可以在涉及到超级管理员的地方重新实现一下,然后再原有的实现类中只需要专注我们权限架构中的规则进行数据库查询等操作了,而不需要向我一开始那样为超级管理员进行特殊操作。如果后续我们需要为特殊用户进行特殊开发。我们就可以扩展我们的表达式解析然后再开发我们备用接口就可以了。
  • 这个思路主要来自于Spring Cloud 中的OpenFeign 的容灾降级的思路。

以上就是springboot通用分支处理超级管理员权限逻辑的详细内容,更多关于springboot通用分支处理权限的资料请关注我们其它相关文章!

(0)

相关推荐

  • springboot+springsecurity如何实现动态url细粒度权限认证

    谨记:Url表只储存受保护的资源,不在表里的资源说明不受保护,任何人都可以访问 1.MyFilterInvocationSecurityMetadataSource 类判断该访问路径是否被保护 @Component //用于设置受保护资源的权限信息的数据源 public class MyFilterInvocationSecurityMetadataSource implements FilterInvocationSecurityMetadataSource { @Bean public An

  • Springboot+Spring Security实现前后端分离登录认证及权限控制的示例代码

    目录 前言 本文主要的功能 一.准备工作 1.统一错误码枚举 2.统一json返回体 3.返回体构造工具 4.pom 5.配置文件 二.数据库表设计 初始化表数据语句 三.Spring Security核心配置:WebSecurityConfig 四.用户登录认证逻辑:UserDetailsService 1.创建自定义UserDetailsService 2.准备service和dao层方法 五.用户密码加密 六.屏蔽Spring Security默认重定向登录页面以实现前后端分离功能 1.实

  • SpringBoot整合Security实现权限控制框架(案例详解)

    目录 一.前言 二.环境准备 2.1.数据库表 四.测试 五.总结 我想每个写项目的人,都肯定会遇到控制权限这个问题. 例如这个这个链接只能管理员访问,那个链接丫只能超级管理员访问等等,实现方式也有多种多样,控制的粒度也不一样. 以前刚学的时候,不会框架,大都是手写注解+过滤器来进行权限的控制,但这样增加了过滤器的负担.用起来也会稍微有些麻烦,粒度不太好控制. 用框架的话,就是封装了更多的操作,让一切更简单吧.当然不局限于Security,还有像Shiro安全框架,这两种非常常见. 一起加油吧!

  • Springboot+mybatis-plus+注解实现数据权限隔离

    目录 1.创建注解 2. 具体实现 1.创建注解 当此注解打在类上,不需要传参,该类下所有查询接口开启数据隔离:打在方法上默认开启数据隔离,传参为false则该方法关闭验证 /** * 数据权限验证注解 * @author xiaohua * @date 2021/6/23 */ @Documented @Target({METHOD, ANNOTATION_TYPE, TYPE}) @Retention(RUNTIME) public @interface DataPermission { /

  • SpringBoot如何整合Springsecurity实现数据库登录及权限控制

    目录 第一步 第二步是封装一个自定义的类 第三步, 我们需要判断密码啦 总结 我们今天使用SpringBoot来整合SpringSecurity,来吧,不多BB 首先呢,是一个SpringBoot 项目,连接数据库,这里我使用的是mybaties.mysql, 下面是数据库的表 DROP TABLE IF EXISTS `xy_role`; CREATE TABLE `xy_role` ( `xyr_id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键i

  • SpringBoot整合Shiro实现权限控制的代码实现

    1.SpringBoot整合Shiro Apache Shiro是一个强大且易用的Java安全框架,执行身份验证.授权.密码和会话管理. 1.1.shiro简介 shiro有个核心组件,分别为Subject.SecurityManager和Realms Subject:相当于当前操作的"用户",这个用户不一定是一个具体的人,是一个抽象的概念,表明的是和当前程序进行交互的任何东西,例如爬虫.脚本.等等.所有的Subject都绑定到SecurityManager上,与 Subject 的所

  • springboot通用分支处理超级管理员权限逻辑

    目录 前言 自定义注解 SuperDirection SuperDirectionHandler 作用 位置 切面 条件判断 表达式解析 演示使用 controller service 测试 总结 前言 当引入登录模块后我们需要做菜单.而菜单自然需要权限的参与,我们在springboot中设计的权限细粒度还算是比较细的.当我们查询菜单是需要根据权限查找对应的菜单.但是在springboot中我设计了一个底层超级管理员 先来看看我一开始实现这个超级管理员菜单获取的部分代码 if (Security

  • Android获取超级管理员权限的实现

    1.定义特殊的广播接收者,系统超级管理员的广播接收者 public class MyDeviceAdminReceiver extends DeviceAdminReceiver{ @Override public void onReceive(Context context,Intent intent){ //TODO } } 2.在AndroidManifest.xml文件中,注册超级管理员的广播接收者 <receiver android:name="com.example.recei

  • SpringBoot如何通过自定义注解实现权限检查详解

    前言 最近开发了一个接口,完成后准备自测时,却被拦截器拦截了,提示:(AUTH-NO)未能获得有效的请求参数!怎么会这样呢? 于是我全局搜了这个提示语,结果发现它被出现在一个Aspect类当中了,并且把一个 @interface 作为了一个切点,原来这里利用了Spring AOP面向切面的方式进行权限控制. SpringBoot通过自定义注解实现日志打印可参考:SpringBoot通过自定义注解实现日志打印 正文 Spring AOP Spring AOP 即面向切面,是对OOP面向对象的一种延

  • SpringBoot整合Shiro框架,实现用户权限管理

    一.Shiro简介 核心角色 1)Subject:认证主体 代表当前系统的使用者,就是用户,在Shiro的认证中,认证主体通常就是userName和passWord,或者其他用户相关的唯一标识. 2)SecurityManager:安全管理器 Shiro架构中最核心的组件,通过它可以协调其他组件完成用户认证和授权.实际上,SecurityManager就是Shiro框架的控制器. 3)Realm:域对象 定义了访问数据的方式,用来连接不同的数据源,如:关系数据库,配置文件等等. 核心理念 Shi

  • Springboot整合Shiro实现登录与权限校验详细解读

    目录 Springboot-cli 开发脚手架系列 简介 前言 1. 环境 2. 简介 3. Realm配置 4. 核心配置 5. 接口编写 6. 网页资源 7. 效果演示 8. 源码分享 Springboot-cli 开发脚手架系列 Springboot优雅的整合Shiro进行登录校验,权限认证(附源码下载) 简介 Springboo配置Shiro进行登录校验,权限认证,附demo演示. 前言 我们致力于让开发者快速搭建基础环境并让应用跑起来,提供使用示例供使用者参考,让初学者快速上手. 本博

  • SpringBoot详解shiro过滤器与权限控制

    目录 shiro过滤器 权限控制 动态配置权限 shiro过滤器 首先从客户端发来的所有请求都经过Shiro过滤器,如果用户没有认证的都打回去进行认证,认证成功的,再判断是否具有访问某类资源(公有资源,私有资源)的权限,如果没有权限,访问失败;如果有权限访问成功.注意:客户端传来的token要和realm中的认证信息进行相同规则的比较(加密算法要一致). 常见过滤器: 1.在shiro配置类中配置,使用 filterFactoryBean.setFilterChainDefinitionMa()

  • CMD命令行中以管理员权限启动应用程序实现方法

    很多时候我们需要管理员权限来运行bat那么就需要结合vbscript来实现了 方法一: %1 mshta vbscript:CreateObject("Shell.Application").ShellExecute("cmd.exe","/c %~s0 ::","","runas",1)(window.close)&&exit 常用 @echo off mode con lines=30

  • 在vista中快速以管理员权限打开命令行窗口

    我们都知道,在Windows Vista中,当打开命令行窗口时,即使当前登录用户具有管理员身份,但其以默认的标准用户权限运行,这在很多时候带来不便,尤其当进行系统级设置时,--事实上,我们在操作中大部分弃图形界面而使用命令行的情况便是进行系统级设置,毕竟,图形界面虽然易于使用易于上手,但往往不具有效率,如需多次点击等十分繁琐--同时,Windows Vista中的UAC机制也使得在进行系统设置时需频繁地通过UAC弹出窗口提升操作者权限,有时甚至会到烦人的程度. 因此,直接以管理员权限打开命令行窗

  • 通过U盘实现本机添加管理员权限

    本机添加管理员权限 1,你得做一个U盘启动盘(解压镜像文件到U盘里面,有六个字文件) 2,插入U盘,设置通过U盘启动,进入DOS界面,一次打开C:\windows\system32 3,ren osk.exe osk0.exe ren cmd.exe osk.exe 4,点击osk(虚拟键盘),这个时候你会惊奇的发现,你打开的是dos操作界面!! 5.解析来该给本机添加账户了 复制代码 代码如下: net user account password /add net localgroup adm

  • Linux给普通用户加超级用户权限的方法

    问题假设用户名为:ali如果用户名没有超级用户权限,当输入 sudo + 命令 时, 系统提示: 复制代码 代码如下: ali is not in the sudoers file.  This incident will be reported. 解决1. 进入超级用户模式.即输入"su",系统会让你输入超级用户密码,输入密码后就进入了超级用户模式. 2. 添加文件的写权限. 复制代码 代码如下: chmod u+w /etc/sudoers 3. 编辑/etc/sudoers文件.

随机推荐