按钮级别的动态权限实现方案【原创思想】

本文介绍了一种动态权限实现方案,通过AOP实现按钮级别的权限校验,无需预先配置接口和按钮。核心类包括Action、Menu和Role,权限配置通过注解定义在接口上,启动时自动整理权限表,用户登录时生成权限树,接口请求时进行权限校验。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

按钮基本的权限校验实现


目录

简述

核心类

一、Action 类

二、Menu 类

三、Role 类

分析

权限配置定义和读取

一、Action配置

二、菜单配置

三、配置读取生成权限表

提供接口配置角色

权限校验

登录

结尾


 

简述

    哈哈哈哈哈哈,我答应会经常更新的。很好,再一次做不到,打脸!来来来写写博客吹吹牛逼来了!

    是这样的目前很多业务系统都希望系统的权限是可以动态可调整的,而且还希望系统的权限可以精确到按钮级别的。通常会这样做先做个按钮和菜单的功能配置表,去控制前端按钮的显示与否。然后再做一个接口管理表,管理所有接口在某个角色当中是否有权限调用。而且我们需要通过前端页面去配置这些接口和按钮。

    今天就说一下我是怎么去实现,不需要去配置有什么接口,有什么按钮的动态权限,同时我也会基于AOP去实现权限认证。


核心类

一、Action 类

主要是记录系统里面有多少个操作,每个接口就算一个操作,我们将功能按钮和对应的接口抽象成一个Action 每一个Action都需要定义一个全局唯一的ActionId,Action还关联着对应的菜单 menuId(主要生成权限树)。

@Entity
@Table(name = "T_ACTION")
public class Action {

    public static final int STATUS_ENABLE = 1;
    public static final int STATUS_DISABLE = 0;


    /**
     * 行为ID 全局唯一 英文标识
     */
    @Id
    @Column(nullable = false)
    private String actionId;

    /**
     * 行为名称 主要用作显示
     */
    @Column(nullable = false)
    private String actionName;

    /**
     * 行为对应的接口URI
     */
    @Column(nullable = false, length = 256)
    private String actionUri;

    /**
     * 行为所属的菜单ID
     */
    @Column(nullable = false)
    private String menuId;

    @Column(nullable = false, length = 6)
    private int status;
}

二、Menu 类

菜单对象,主要用于是否需要显示前端菜单项的。判断可以通过菜单以及子孙菜单是否有相应的action权限,如果当前角色 一个action的权限都没有那就直接将菜单隐藏,这样在返回给前端的权限树当中,就不会有相应的菜单。

Entity
@Table(name = "T_MENU")
public class Menu {

    public static final int STATUS_ENABLE = 1;
    public static final int STATUS_DISABLE = 0;

    public Menu() {

    }

    public Menu(String menuId, String menuName, String parentMenuId) {
        this.menuId = menuId;
        this.menuName = menuName;
        this.parentMenuId = parentMenuId;
        this.status = STATUS_ENABLE;
    }

    /**
     * 菜单ID 全局唯一标识
     */
    @Id
    @Column(nullable = false)
    private String menuId;

    /**
     * 菜单名称 用于显示
     */
    @Column(nullable = false)
    private String menuName;

    /**
     * 父菜单ID 如果没有父级菜单ID 即为顶级菜单
     */
    private String parentMenuId;

    /**
     * 权限状态如果为0则功能已经不存在 或者 已经被取消权限判断
     */
    @Column(nullable = false, length = 6)
    private int status;

}

三、Role 类

Role 主要是配置和管理有那些求权限,同时就是关联着相应的管理员用户。

@Entity
@Table(name = "T_ROLE")
public class Role {

    public static final int STATUS_DISABLE = 0;

    public static final int STATUS_ENABLE = 1;

    @Id
    @GeneratedValue
    @Column(nullable = false)
    private Long roleId;

    /**
     * 角色
     */
    @Column(nullable = false)
    private String name;

    /**
     * 角色描述
     */
    @Column(nullable = false,length = 300)
    private String description;

    /**
     * 角色状态
     */
    @Column(nullable = false)
    private int status;

    /**
     * 角色所对应的权限
     */
    @ManyToMany(targetEntity = Action.class)
    @JoinTable(name = "T_ACTION_ROLE", joinColumns = {@JoinColumn(name = "ROLE_ID")}, inverseJoinColumns = {@JoinColumn(name = "ACTION_ID")})
    private Set<Action> actions;

    @OneToMany(mappedBy = "role")
    private Set<AdminUser> adminUsers;

    /**
     * 创建角色的时间
     */
    @Column(nullable = false)
    private Date createTime;

}

分析

目前我们要做到动态权限要做到几个地方:

  1. 做到接口即Action,定义了接口就等于定义了相关的Action功能,之后我们会通过这个Action去控制权限;
  2. 需要配置菜单,动态关联上对应的Action;
  3. 每次启动服务,可以检查整理出最新的action列表(与你的接口变更一致)和 menu列表;
  4. 用户登录的时候,将用户对应角色拥有的菜单和对应的action 组织成一个权限树,返回给前端渲染菜单和按钮;
  5. 用户请求接口时候,将会通过AOP读取接口定义的ActionId,通过用户对应关系获得该用户的角色是否有权限访问该接口;
  6. 提供接口配置相应的角色权限;

权限配置定义和读取

一、Action配置

我们会定义一个annotation,通过annotation配置到对应的接口上,用作actionId定义和AOP校验的切入点。

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Permission {

    String actionId();

    String menuId();

    /**
     * 功能名称
     * @return
     */
    String name();

}

另外还有一个是不检查权限,只是需要当前用户一定是已经登录并且可以获得其用户信息。我将会定义另外一个annotation进行判断


@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Authorization {
}

定义完成这两个annotation之后,我将使用这两个annotation定义在ResController对应的方法当中。使用方法如下:

@RestController
@RequestMapping("/decorate")
public class DecorateController {
    @Autowired
    private IDecorateService decorateService;

    
    @GetMapping("/view")
    @Permission(actionId = "decorateManage_viewBtn", menuId = "decorateManage", name = "查看小程序装修")
    public ResponseBasic<DecorateRsp> view() {
        return ResponseBasic.ok(decorateService.view());
    }
}

可以看到我在/view 这个接口上面,定义了一个名称为 查看小程序装修 actionId 为 decorateManage_viewBtn 属于 decorateManage 菜单的权限,这个权限将会是全局唯一的。同时我们这个actionId 在用户登录的时候用户拥有什么权限actionId 会全部给前端渲染页面。

二、菜单配置

我们需要定义个菜单配置的类,其实只是配置一个bean将所有配置的菜单保存起来

/**
 * Created by TONY YAN
 */
public class PermissionConfig {

    private Set<Menu> menus = new HashSet<>();

    public Set<Menu> getMenus() {
        return this.menus;
    }

    public PermissionConfig addMenu(Menu menu) {
        menus.add(menu);
        return this;
    }

}

然后就开始配置菜单,在其中一个配置类里面配置定义好的PermissionConfig,定义好各个菜单的名称和menuId,还有就是就是父级的menuId

@Bean
    public PermissionConfig menuDefinition() {
        PermissionConfig permissionConfig = new PermissionConfig();
        permissionConfig.addMenu(new Menu("staffManageIndex", "员工管理", null));
        permissionConfig.addMenu(new Menu("roleManage", "角色管理", "staffManageIndex"));
        permissionConfig.addMenu(new Menu("staffManage", "员工管理", "staffManageIndex"));

        permissionConfig.addMenu(new Menu("storeManage", "门店管理", "staffManageIndex"));
        permissionConfig.addMenu(new Menu("orderManageIndex", "订单管理", null));
        permissionConfig.addMenu(new Menu("orderSurvey", "订单概况", "orderManageIndex"));
        permissionConfig.addMenu(new Menu("orderManage", "所有订单", "orderManageIndex"));
        permissionConfig.addMenu(new Menu("goodsManage", "商品管理", null));
        permissionConfig.addMenu(new Menu("memberManage", "会员管理", null));
        permissionConfig.addMenu(new Menu("decorateManage", "小程序装修", null));
        permissionConfig.addMenu(new Menu("cardManage", "礼品卡管理", null));
        return permissionConfig;
    }

三、配置读取生成权限表

OK,那我们还需要提供一个借口给前端用户,选择某个角色应该拥有什么权限。所以我们需要每次启动SpringBoot应用的时候,将所有其本身定义的所有权限action给记录起来。需要完成这一步的思路是这样的,我们需要在SpringBoot启动后,然后扫描Spring容器里面所有的Bean找到对应的@Permission的配置。上代码:

1、我们需要实现initializingBean接口,在afterPropertiesSet 方法里面获得Spring的context,然后通过获得所有的RestController类的Bean。

/**
 * Created by TONY YAN
 */
@Component
public class PermissionInitializing implements InitializingBean {

    @Aut
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值