Spring Security下你要怎么设计表结构?


theme: vue-pro

前言

就目前所学, 很多东西都会了, 但是还不够实战话, 比如, 数据库表结构怎么设计? 有什么理论提供参考?

本章内容

  • 权限模型
  • 表结构设计

权限模型

需要理论提供参考, 你才能够了解别人是怎么设计的

是什么?

权限模型是一种指导思想也是一种权限模型, 知道程序员如何开发出高效的, 易于维护的权限管理系统

常见的权限模型

DAC(看看就好)

DAC (Discretionary access control)指自主访问控制。它是 Tnusted Computer SystemEvaluation Criteria (TCSEC)定义的一种访问控制模型。在这种访问控制模型中,系统会根据被操作对象的权限控制列表中的信息,来决定当前用户能够对其进行哪些操作用户可以将其具备的权限直接或者间接授予其他用户,这也是其称为自主访问控制的原因。
自主访问控制经常与强制访问控制(MAC)对比。

有点像已拥有权限的用户, 发布邀请码给未注册的用户, 让其注册, 否则没有邀请码无法注册论坛

MAC(看看就好)

MAC (Mandatory access control)指强制访问控制,也叫非自主访问控制。这种访问控制方式可以限制主体对对象或目标执行某种操作的能力。通过强制访问控制,安全策略由安全策略管理员集中控制,用户无权覆盖策略。

这种能力有点像, 小区门禁, 有小区物业统一给予小区业主访问某栋楼的权限

ABAC(看看就好)

ABAC (Attribute-Based Access Control)基于属性的访问控制,有时也被称为PBAC(Policy-Based Access Control)或者CBAC (Claims-Based Access Control)
ABAC也被称为下一代权限管理模型。
基于属性的访问控制中一般来说包含四类属性:用户属性、环境属性、操作属性以及资源属性,通过动态计算一个或者一组属性是否满足某一条件来进行授权。
当然,上面介绍的这三种权限模型并非本章重点,因为我们在Java 企业级开发中很少会用到它们。
在企业级开发领域,目前最为流行的权限管理模型当属RBAC。除了RBAC之外,还有一个ACL权限模型,Spring Security 中针对ACL也提供了相关的依赖,所以本章重点介绍这两种权限模型。

ACL(看看就好)

是什么?

一种古老的权限访问模型, 该模型针对资源配置权限, 用户/角色必须拥有某些权限, 才能够访问某些资源

ACL的一个核心思路就是:将某个对象的某种权限授予某个用户或某种角色,它们之间的关系是多对多,即一个用户/角色可以具备某个对象的多种权限,某个对象的权限也可以被多个用户/角色所持有。

缺点

ACL是一种粒度非常细的权限控制,它可以精确到某一个资源的某一个权限。这些权限数据都记录在数据库中,这带来的另外一个问题就是需要维护的权限数据量非常庞大,特别是对于一些大型系统而言,这一问题尤其突出,大量的数据需要维护可能会造成系统性能下降

使用场景

对于一些简单的小型系统而言,使用ACL还是可以的,没有任何问题。

Spring Security对ACL提供了支持, 我们只要导入spring-security-acl依赖, 那么就可以在项目中使用了

核心概念

Sid

Sid表示用户和角色

注意这里是 和 不是 或

它拥有两种:

  • GrantedAuthoritySid(角色)
  • PrincipalSid(用户)
public class SidRetrievalStrategyImpl implements SidRetrievalStrategy {

   private RoleHierarchy roleHierarchy = new NullRoleHierarchy();

   public SidRetrievalStrategyImpl() {
   }

   public SidRetrievalStrategyImpl(RoleHierarchy roleHierarchy) {
      Assert.notNull(roleHierarchy, "RoleHierarchy must not be null");
      this.roleHierarchy = roleHierarchy;
   }

   @Override
   public List<Sid> getSids(Authentication authentication) {
      Collection<? extends GrantedAuthority> authorities = this.roleHierarchy
            .getReachableGrantedAuthorities(authentication.getAuthorities());
      List<Sid> sids = new ArrayList<>(authorities.size() + 1);
      sids.add(new PrincipalSid(authentication));
      for (GrantedAuthority authority : authorities) {
         sids.add(new GrantedAuthoritySid(authority));
      }
      return sids;
   }

}

PrincipalSid == authentication.getName 用户

GrantedAuthoritySid 约等于 grantedAuthority.getAuthority() 角色

在 ACL 中, 需要查看用户的信息, 就直接查看 Sid

ObjectIdentity

这是我们要操作的对象.

将操作对象和操作方法分开

ObjectIdentity 中有两个关键方法,getTypegetIdentifier。一般来说,getType方法返回资源类的全路径,例如 org.javaboy.acl.model.User: getIdentifier方法则返回资源的id。通过这两个方法,就能够锁定一个具体的对象。

Acl对象

三个部分:

  1. ObjectIdentity: 操作对象
  2. Sid: Acl对象持有者
  3. ACE: AccessContolEntry, 代表权限记录, 里面存储了 Sid 和 permission对象

Sid可以删除Acl对象

这里说白了

  • Sid 就是 ACL 的 id, 也就是 ACL的持有者(持有者)
  • ObjectIdentity对象就是我们要操作的对象(目标)
  • ACE则表示权限, Sid 需要什么权限 才能够操作 ObjectIdentity对象(权限)
ACE对象

一个ACL对象可以持有多个权限, 也就是 ACE

ACL对象就是一个描述对象, 表述了 Sid 持有对 ObjectIdentity 的哪些 ACE权限

image-20221205133914703

permission对象

是一个权限对象

在 spring security中最多支持 32 中权限, 而 spring security 默认支持5种权限:

public class BasePermission extends AbstractPermission {

   public static final Permission READ = new BasePermission(1 << 0, 'R'); // 1

   public static final Permission WRITE = new BasePermission(1 << 1, 'W'); // 2

   public static final Permission CREATE = new BasePermission(1 << 2, 'C'); // 4

   public static final Permission DELETE = new BasePermission(1 << 3, 'D'); // 8

   public static final Permission ADMINISTRATION = new BasePermission(1 << 4, 'A'); // 16

   protected BasePermission(int mask) {
      super(mask);
   }

   protected BasePermission(int mask, char code) {
      super(mask, code);
   }

}

如果 Spring security 默认提供的 五种 权限不足以解决我们的业务, 可以自定义实习 BasePermission

AclService

AclService 定义了一些解析 acl对象的方法, 例如根据OjbectIdentity 查询到对应的 Acl对象

image-20221205134642080

提供了三个实现

image-20221205134811800

MutableAclService 接口提供增删改方法

JdbcAclService 提供查询acl 的方法

JdbcMutableAclService整合了查询和增删改的功能(直接使用这个就成)

ACL数据库分析

image-20221205140356982

acl提供了很多数据库建表语句, 直接找到对应的sql 拿来用也行

image-20221205172050970

表结构和表数据

/*
 Navicat Premium Data Transfer

 Source Server         : localhost
 Source Server Type    : MySQL
 Source Server Version : 80028 (8.0.28)
 Source Host           : localhost:3306
 Source Schema         : acl

 Target Server Type    : MySQL
 Target Server Version : 80028 (8.0.28)
 File Encoding         : 65001

 Date: 05/12/2022 21:22:30
*/

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for acl_class
-- ----------------------------
DROP TABLE IF EXISTS `acl_class`;
CREATE TABLE `acl_class`  (
  `id` bigint UNSIGNED NOT NULL AUTO_INCREMENT,
  `class` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '资源类的全路径名',
  PRIMARY KEY (`id`) USING BTREE,
  UNIQUE INDEX `uk_acl_class`(`class` ASC) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 2 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of acl_class
-- ----------------------------
INSERT INTO `acl_class` VALUES (1, 'com.zhazha.acl.entity.NoticeMessage');

-- ----------------------------
-- Table structure for acl_entry
-- ----------------------------
DROP TABLE IF EXISTS `acl_entry`;
CREATE TABLE `acl_entry`  (
  `id` bigint UNSIGNED NOT NULL AUTO_INCREMENT,
  `acl_object_identity` bigint UNSIGNED NOT NULL COMMENT '关联acl_object _identity.id。\n',
  `ace_order` int NOT NULL COMMENT '权限顺序。acl_object_identity和ace_order的组合要唯一。\n',
  `sid` bigint UNSIGNED NOT NULL COMMENT '关联acl_sid.id,该字段关联一个具体的用户/角色。\n',
  `mask` int UNSIGNED NOT NULL COMMENT '权限掩码',
  `granting` tinyint(1) NOT NULL COMMENT '表示当前记录是否生效。\n',
  `audit_success` tinyint(1) NOT NULL COMMENT '审计信息',
  `audit_failure` tinyint(1) NOT NULL COMMENT '审计信息',
  PRIMARY KEY (`id`) USING BTREE,
  UNIQUE INDEX `unique_acl_entry`(`acl_object_identity` ASC, `ace_order` ASC) USING BTREE,
  INDEX `fk_acl_entry_acl`(`sid` ASC) USING BTREE,
  CONSTRAINT `fk_acl_entry_acl` FOREIGN KEY (`sid`) REFERENCES `acl_sid` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT,
  CONSTRAINT `fk_acl_entry_object` FOREIGN KEY (`acl_object_identity`) REFERENCES `acl_object_identity` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT
) ENGINE = InnoDB AUTO_INCREMENT = 23 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of acl_entry
-- ----------------------------
INSERT INTO `acl_entry` VALUES (15, 14, 0, 2, 1, 1, 0, 0);
INSERT INTO `acl_entry` VALUES (16, 14, 1, 2, 2, 1, 0, 0);
INSERT INTO `acl_entry` VALUES (22, 18, 0, 1, 4, 1, 0, 0);

-- ----------------------------
-- Table structure for acl_object_identity
-- ----------------------------
DROP TABLE IF EXISTS `acl_object_identity`;
CREATE TABLE `acl_object_identity`  (
  `id` bigint UNSIGNED NOT NULL AUTO_INCREMENT,
  `object_id_class` bigint UNSIGNED NOT NULL COMMENT '关联acl_class.id,即需要进行权限控制的类信息。\n',
  `object_id_identity` varchar(36) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '需要控制的对象的id,和object_id_class字段共同锁定一个具体对象。\n',
  `parent_object` bigint UNSIGNED NULL DEFAULT NULL COMMENT '父对象ID,关联一条acl_object_identity记录。\n',
  `owner_sid` bigint UNSIGNED NULL DEFAULT NULL COMMENT '这个ACL记录拥有者的Sid,拥有者可以对该ACL记录执行更新、删除等操作。\n',
  `entries_inheriting` tinyint(1) NOT NULL COMMENT '是否需要继承父对象的权限。\n',
  PRIMARY KEY (`id`) USING BTREE,
  UNIQUE INDEX `uk_acl_object_identity`(`object_id_class` ASC, `object_id_identity` ASC) USING BTREE,
  INDEX `fk_acl_object_identity_parent`(`parent_object` ASC) USING BTREE,
  INDEX `fk_acl_object_identity_owner`(`owner_sid` ASC) USING BTREE,
  CONSTRAINT `fk_acl_object_identity_class` FOREIGN KEY (`object_id_class`) REFERENCES `acl_class` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT,
  CONSTRAINT `fk_acl_object_identity_owner` FOREIGN KEY (`owner_sid`) REFERENCES `acl_sid` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT,
  CONSTRAINT `fk_acl_object_identity_parent` FOREIGN KEY (`parent_object`) REFERENCES `acl_object_identity` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT
) ENGINE = InnoDB AUTO_INCREMENT = 19 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of acl_object_identity
-- ----------------------------
INSERT INTO `acl_object_identity` VALUES (14, 1, '1', NULL, 13, 1);
INSERT INTO `acl_object_identity` VALUES (18, 1, '99', NULL, 13, 1);

-- ----------------------------
-- Table structure for acl_sid
-- ----------------------------
DROP TABLE IF EXISTS `acl_sid`;
CREATE TABLE `acl_sid`  (
  `id` bigint UNSIGNED NOT NULL AUTO_INCREMENT,
  `principal` tinyint(1) NOT NULL COMMENT 'Sid的类型, 1表示PrincipalSid, 0表示GrantedAuthoritySid',
  `sid` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '用户名或者角色名',
  PRIMARY KEY (`id`) USING BTREE,
  UNIQUE INDEX `unique_acl_sid`(`sid` ASC, `principal` ASC) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 14 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of acl_sid
-- ----------------------------
INSERT INTO `acl_sid` VALUES (2, 1, 'hr');
INSERT INTO `acl_sid` VALUES (1, 1, 'manager');
INSERT INTO `acl_sid` VALUES (3, 0, 'ROLE_EDITOR');
INSERT INTO `acl_sid` VALUES (13, 1, 'zhazha');

-- ----------------------------
-- Table structure for system_message
-- ----------------------------
DROP TABLE IF EXISTS `system_message`;
CREATE TABLE `system_message`  (
  `id` bigint NOT NULL AUTO_INCREMENT,
  `content` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 4 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of system_message
-- ----------------------------
INSERT INTO `system_message` VALUES (1, '111');
INSERT INTO `system_message` VALUES (2, '222');
INSERT INTO `system_message` VALUES (3, '333');

SET FOREIGN_KEY_CHECKS = 1;

怎么玩?

@Getter
@Setter
@ToString
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class NoticeMessage implements Serializable {
   private Long id;
   
   private String content;
   
   private static final long serialVersionUID = 1L;
}

@Configuration
@EnableCaching
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
@RequiredArgsConstructor
public class AclConfig extends GlobalMethodSecurityConfiguration {
	
	private final DataSource dataSource;
	
	private final CacheManager cacheManager;
	
	@Override
	protected MethodSecurityExpressionHandler createExpressionHandler() {
		DefaultMethodSecurityExpressionHandler expressionHandler = new DefaultMethodSecurityExpressionHandler();
		AclPermissionEvaluator permissionEvaluator = new AclPermissionEvaluator(this.aclService());
		expressionHandler.setPermissionEvaluator(permissionEvaluator);
		return expressionHandler;
	}
	
	/**
	 * 判断当前认证主体是否拥有修改ACL权限, 准确来说是三种权限: 修改ACL的owner, 修改ACL的审计信息, 修改ACE本身
	 * 这个接口只有一个实现类, 我们创建实例, 可以传入三个参数, 也可以传入一个参数, 表示一个角色可以做三件事情
	 *
	 * @return
	 */
	@Bean
	public AclAuthorizationStrategy aclAuthorizationStrategy() {
		return new AclAuthorizationStrategyImpl(new SimpleGrantedAuthority("ROLE_ADMIN"));
	}
	
	/**
	 * 这个接口提供了一个 isGranted 的方法, 这个方法真正进行了权限比较的方法, 改接口只有一个实现类
	 *
	 * @return
	 */
	@Bean
	public PermissionGrantingStrategy permissionGrantingStrategy() {
		return new DefaultPermissionGrantingStrategy(new ConsoleAuditLogger());
	}
	
	/**
	 * 在 acl 中权限总是查询数据库, 则会导致权限问题, 所以引入了 ehcache作缓存
	 *
	 * @return
	 */
	@Bean
	public AclCache aclCache() {
		return new SpringCacheBasedAclCache(
				cacheManager.getCache("AclCache"),
				permissionGrantingStrategy(), aclAuthorizationStrategy()
		);
	}
	
	@Bean
	public LookupStrategy lookupStrategy() {
		return new BasicLookupStrategy(dataSource, aclCache(), aclAuthorizationStrategy(), new ConsoleAuditLogger());
	}
	
	@Bean
	public AclService aclService() {
		return new JdbcMutableAclService(dataSource, lookupStrategy(), aclCache());
	}
	
	/**
	 * 为表达式提供支持
	 *
	 * @return
	 */
	@Bean
	public PermissionEvaluator permissionEvaluator() {
		return new AclPermissionEvaluator(aclService());
	}
	
}
@Resource
private NoticeMessageService noticeMessageService;
@Resource
private JdbcMutableAclService jdbcMutableAclService;

@Test
@WithMockUser(username = "manager")
void findAll() {
   List<NoticeMessage> all = noticeMessageService.findAll();
   Assertions.assertEquals(0, all.size());
}

@WithMockUser(username = "zhazha")
@Transactional
@Rollback(value = false)
@Test
public void test01() throws Exception {
   ObjectIdentityImpl objectIdentity = new ObjectIdentityImpl(NoticeMessage.class, 5);
   Permission permission = BasePermission.READ;
   MutableAcl acl = jdbcMutableAclService.createAcl(objectIdentity);
   acl.insertAce(acl.getEntries().size(), permission, new PrincipalSid("hr1"), true);
   jdbcMutableAclService.updateAcl(acl);
}

@WithMockUser(username = "hr")
@Test
public void test02() throws Exception {
   List<NoticeMessage> all = noticeMessageService.findAll();
   System.out.println(all);
   NoticeMessage noticeMessage = noticeMessageService.selectById(1L);
   System.out.println(noticeMessage);
}

@WithMockUser(username = "zhazha")
@Transactional
@Rollback(value = false)
@Test
public void test03() throws Exception {
   ObjectIdentityImpl objectIdentity = new ObjectIdentityImpl(NoticeMessage.class, 100);
   Permission permission = BasePermission.CREATE;
   MutableAcl acl = jdbcMutableAclService.createAcl(objectIdentity);
   acl.insertAce(acl.getEntries().size(), permission, new PrincipalSid("manager"), true);
   jdbcMutableAclService.updateAcl(acl);
}

@WithMockUser(username = "manager")
@Test
public void test04() throws Exception {
   NoticeMessage noticeMessage = new NoticeMessage();
   noticeMessage.setContent("999");
   noticeMessageService.insert(noticeMessage);
}

优缺点

ACL权限颗粒度特别细致, 比较麻烦, 而且扩展性也比较差

ACL缺点在于由于用户和权限直接挂钩,导致在授予时的复杂性,虽然可以利用组来简化这个复杂性,但仍然会导致系统不好理解,而且在取出判断用户是否有该权限时比较的困难,一定程度上影响了效率。

RBAC(推荐这种方式)

RBAC的核心是用户只和角色关联,而角色代表对了权限,这样设计的优势在于使得对用户而言,只需角色即可以,而某角色可以拥有各种各样的权限并可继承。

RBAC权限模型将用户按角色进行归类,通过用户的角色来确定用户对某项资源是否具备操作权限。RBAC简化了用户与权限的管理,它将用户与角色关联、角色与权限关联、权限与资源关联,这种模式使得用户的授权管理变得非常简单和易于维护。

RBAC权限模型有三原则

  • 最小权限: 给角色配置的权限是其完成任务所需要的最小权限集合。
  • 职责分离: 通过相互独立互斥的角色来共同完成任务。
  • 数据抽象: 通过权限的抽象来体现,RBAC支持的数据抽象程度与RBAC的实现细节有关。

为什么要这么玩?

存在多个用户拥有相同的权限,在分配的时候就要分别为这几个用户指定相同的权限,修改时也要为这几个用户的权限进行一一修改。有了角色后,我们只需要为该角色制定好权限后,将相同权限的用户都指定为同一个角色即可,便于权限管理。

权限过多而复杂, 权限的颗粒度特别细, 比较难以管理, 直接跟用户挂钩将导致用户权限杂乱, 所以需要添加一个权限集合, 也就是角色

对于批量的用户权限调整,只需调整用户关联的角色权限,无需对每一个用户都进行权限调整,既大幅提升权限调整的效率,又降低了漏调权限的概率。

调整权限, 只需要调整角色便可

img

RBAC权限模型分类

RBAC总共有4种情况

RBAC0模型

最简单的用户、角色、权限模型。这里面又包含了2种:

  1. 用户和角色是多对一关系,即:一个用户只充当一种角色,一种角色可以有多个用户担当。
  2. 用户和角色是多对多关系,即:一个用户可同时充当多种角色,一种角色可以有多个用户担当。

img

张三既是行政,也负责财务工作,那张三就同时拥有行政和财务两个角色的权限。

RBAC1模型

相对于RBAC0模型,增加了子角色,引入了继承概念,即子角色可以继承父角色的所有权限。

img

**使用场景:**如某个业务部门,有经理、主管、专员。主管的权限不能大于经理,专员的权限不能大于主管,如果采用RBAC0模型做权限系统,极可能出现分配权限失误,最终出现主管拥有经理都没有的权限的情况。

RBAC1模型就很好解决了这个问题,创建完经理角色并配置好权限后,主管角色的权限继承经理角色的权限,并且支持在经理权限上删减主管权限。

多了个继承功能, 很好用

RBAC2模型

基于RBAC0模型,增加了对角色的一些限制:角色互斥、基数约束、先决条件角色等。

  • **角色互斥:**角色是有互斥规则的, 比如会计和审核会计的审计员, 这两肯定不能是同一个人, 否则贪污腐败必出。
  • **基数约束:**一个角色被分配的用户数量受限。一个角色的数量是有限的, 一个公司只能有一个老板
  • **先决条件角色:**指要想获得较高的权限,要首先拥有低一级的权限。例如:先有副总经理权限,才能有总经理权限。
  • **运行时互斥:**一个用户有多个角色, 当时在登录后只能选择一种角色(或者几种角色)使用

可以明显看到比较好用的功能

RBAC3模型

称为统一模型,它包含了RBAC1RBAC2,利用传递性,也把RBAC0包括在内,综合了RBAC0RBAC1RBAC2的所有特点

img

RBAC在前面的章节就已经实现了, 所以不需要再去实战

什么是权限?

权限是资源的集合, 这里的资源指的是软件中所有的内容,包括模块、菜单、页面、字段、操作功能(增删改查)等等。

可以将权限分为:页面权限、操作权限和数据权限

**页面权限:**所有系统都是由一个个的页面组成,页面再组成模块,用户是否能看到这个页面的菜单、是否能进入这个页面就称为页面权限。

image-20221206003519540

客户列表、客户黑名单、客户审批页面组成了客户管理这个模块。对于普通用户,不能进行审批操作,即无客户审批页面权限,在他的账号登录后侧边导航栏只显示客户列表、客户黑名单两个菜单。

**操作权限:**用户凡是在操作系统中的任何动作、交互都是操作权限,如增删改查等。

**数据权限:**一般业务管理系统,都有数据私密性的要求:哪些人可以看到哪些数据,不可以看到哪些数据。

简单举个例子:某系统中有销售部门,销售专员负责推销商品,销售主管负责管理销售专员日常工作,经理负责组织管理销售主管作业。

img

按照实际理解,‘销售专员张三’登录时,只能看到自己负责的数据;销售主管2登录时,能看到他所领导的所有业务员负责的数据,但看不到其他团队业务员负责的数据。

换另外一句话就是:我的客户只有我和我的直属上级以及直属上级的领导能看到,这就是我理解的数据权限。

要实现数据权限有多种方式:

  1. 可以利用RBAC1模型,通过角色分级来实现。
  2. 在‘用户-角色-权限’的基础上,增加用户与组织的关联关系,用组织决定用户的数据权限。

具体如何做呢?

①组织层级划分:

img

**②数据可视权限规则制定:**上级组织只能看到下级组织员工负责的数据,而不能看到其他平级组织及其下级组织的员工数据等。

通过以上两点,系统就可以在用户登录时,自动判断要给用户展示哪些数据了。

用户组的使用

当平台用户基数增大,角色类型增多时,如果直接给用户配角色,管理员的工作量就会很大。这时候我们可以引入一个概念“用户组”,就是将相同属性的用户归类到一起。

例如:加入用户组的概念后,可以将部门看做一个用户组,再给这个部门直接赋予角色(1万员工部门可能就几十个),使部门拥有部门权限,这样这个部门的所有用户都有了部门权限,而不需要为每一个用户再单独指定角色,极大的减少了分配权限的工作量。

同时,也可以为特定的用户指定角色,这样用户除了拥有所属用户组的所有权限外,还拥有自身特定的权限。

用户组的优点,除了减少工作量,还有更便于理解、增加多级管理关系等。如:我们在进行组织机构配置的时候,除了加入部门,还可以加入科室、岗位等层级,来为用户组内部成员的权限进行等级上的区分。

实例分析

如何设计RBAC权限系统

首先,我们思考一下一个简单的权限系统应该具备哪些内容?

答案显而易见,RBAC模型:用户-角色-权限。所以最基本的我们应该具备用户、角色、权限这三个内容。

接下来,我们思考,究竟如何将三者关联起来。回顾前文,角色作为枢纽,关联用户、权限。所以在RBAC模型下,我们应该:创建一个角色,并为这个角色赋予相应权限,最后将角色赋予用户

将这个问题抽象为流程,如下图:

img

现在,基本的流程逻辑已经抽象出来了,接下来,分析该如何设计呢?

  • 第一步,需要角色管理列表,在角色管理列表能快速创建一个角色,且创建角色的同时能为角色配置权限,并且支持创建成功的角色列表能随时进行权限配置的的修改;
  • 第二步,需要用户管理列表,在用户管理列表能快速添加一个用户,且添加用户时有让用户关联角色的功能。

简单来说权限系统设计就包含以上两步,接下来为大家进行实例分析。

实例分析

①创建角色列表

img

在角色列表快速创建一个角色:点击创建角色,支持创建角色时配置权限。

img

②创建用户列表

img

在用户列表快速创建一个用户:支持用户关联角色的功能。

img

上述案例是基于最简单的RBAC0模型创建,适用于大部分常规的权限管理系统。

下面再分析一下RBAC1中角色分级具体如何设计。

  1. RBAC0的基础上,加上角色等级这个字段。
  2. 权限分配规则制定:低等级角色只能在高等级角色权限基础上进行删减权限。

具体界面呈现如下图:

img

以上就是简单的RBAC系统设计,若需更复杂的,还请读者根据上面的分析自行揣摩思考,尽管样式不同,但万变不离其宗,理解清楚RBAC模型后,结合自己的业务就可以设计出一套符合自己平台需求的角色权限系统,具体的就不再多阐述了。

部分内容转载自: RBAC模型:基于用户-角色-权限控制的一些思考 | 人人都是产品经理 (woshipm.com)

RBAC数据库设计

RBAC(Role-Based Access Control,基于角色的访问控制),就是用户通过角色与权限进行关联。简单地说,一个用户拥有若干角色,每一个角色拥有若干权限。这样,就构造成“用户-角色-权限”的授权模型。在这种模型中,用户与角色之间,角色与权限之间,一般者是多对多的关系。(如下图)

img

角色是什么?可以理解为一定数量的权限的集合,权限的载体。例如:一个论坛系统,“超级管理员”、“版主”都是角色。版主可管理版内的帖子、可管理版内的用户等,这些是权限。要给某个用户授予这些权限,不需要直接将权限授予用户,可将“版主”这个角色赋予该用户。

当用户的数量非常大时,要给系统每个用户逐一授权(授角色),是件非常烦琐的事情。这时,就需要给用户分组,每个用户组内有多个用户。除了可给用户授权外,还可以给用户组授权。这样一来,用户拥有的所有权限,就是用户个人拥有的权限与该用户所在用户组拥有的权限之和。(下图为用户组、用户与角色三者的关联关系)

img

在应用系统中,权限表现成什么?对功能模块的操作,对上传文件的删改,菜单的访问,甚至页面上某个按钮、某个图片的可见性控制,都可属于权限的范畴。有些权限设计,会把功能操作作为一类,而把文件、菜单、页面元素等作为另一类,这样构成“用户-角色-权限-资源”的授权模型。而在做数据表建模时,可把功能操作和资源统一管理,也就是都直接与权限表进行关联,这样可能更具便捷性和易扩展性。(见下图)

img
请留意权限表中有一列“权限类型”,我们根据它的取值来区分是哪一类权限,如“MENU”表示菜单的访问权限、“OPERATION”表示功能模块的操作权限、“FILE”表示文件的修改权限、“ELEMENT”表示页面元素的可见性控制等。

这样设计的好处有二。其一,不需要区分哪些是权限操作,哪些是资源,(实际上,有时候也不好区分,如菜单,把它理解为资源呢还是功能模块权限呢?)。其二,方便扩展,当系统要对新的东西进行权限控制时,我只需要建立一个新的关联表“权限XX关联表”,并确定这类权限的权限类型字符串。

这里要注意的是,权限表与权限菜单关联表、权限菜单关联表与菜单表都是一对一的关系。(文件、页面权限点、功能操作等同理)。也就是每添加一个菜单,就得同时往这三个表中各插入一条记录。这样,可以不需要权限菜单关联表,让权限表与菜单表直接关联,此时,须在权限表中新增一列用来保存菜单的ID,权限表通过“权限类型”和这个ID来区分是种类型下的哪条记录。

到这里,RBAC权限模型的扩展模型的完整设计图如下:

点击查看原始大小图片
随着系统的日益庞大,为了方便管理,可引入角色组对角色进行分类管理,跟用户组不同,角色组不参与授权。例如:某电网系统的权限管理模块中,角色就是挂在区局下,而区局在这里可当作角色组,它不参于权限分配。另外,为方便上面各主表自身的管理与查找,可采用树型结构,如菜单树、功能树等,当然这些可不需要参于权限分配。

简单设计

img

这部分转载自: RBAC用户权限管理数据库设计-阿里云开发者社区 (aliyun.com)

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
序言 I. 入门 1. 介绍 1.1. Spring Security是什么? 1.2. 历史 1.3. 发行版本号 1.4. 获得Spring Security 1.4.1. 项目模块 1.4.1.1. Core - spring-security-core.jar 1.4.1.2. Web - spring-security-web.jar 1.4.1.3. Config - spring-security-config.jar 1.4.1.4. LDAP - spring-security-ldap.jar 1.4.1.5. ACL - spring-security-acl.jar 1.4.1.6. CAS - spring-security-cas-client.jar 1.4.1.7. OpenID - spring-security-openid.jar 1.4.2. 获得源代码 2. Security命名空间配置 2.1. 介绍 2.1.1. 命名空间的设计 2.2. 开始使用安全命名空间配置 2.2.1. 配置web.xml 2.2.2. 最小 <http>配置 2.2.2.1. auto-config包含了什么? 2.2.2.2. 单和基本登录选项 2.2.3. 使用其他认证提供器 2.2.3.1. 添加一个密码编码器 2.3. 高级web特性 2.3.1. Remember-Me认证 2.3.2. 添加HTTP/HTTPS信道安全 2.3.3. 会话管理 2.3.3.1. 检测超时 2.3.3.2. 同步会话控制 2.3.3.3. 防止Session固定攻击 2.3.4. 对OpenID的支持 2.3.4.1. 属性交换 2.3.5. 添加你自己的filter 2.3.5.1. 设置自定义 AuthenticationEntryPoint 2.4. 保护方法 2.4.1. <global-method-security>元素 2.4.1.1. 使用protect-pointcut添加安全切点 2.5. 默认的AccessDecisionManager 2.5.1. 自定义AccessDecisionManager 2.6. 验证管理器和命名空间 3. 示例程序 3.1. Tutorial示例 3.2. Contacts 3.3. LDAP例子 3.4. CAS例子 3.5. Pre-Authentication例子 4. Spring Security社区 4.1. 任务跟踪 4.2. 成为参与者 4.3. 更多信息 II. 结构和实现 5. 技术概述 5.1. 运行环境 5.2. 核心组件 5.2.1. SecurityContextHolder, SecurityContext 和 Authentication对象 5.2.1.1. 获得当前用户的信息 5.2.2. UserDetailsService 5.2.3. GrantedAuthority 5.2.4. 小结 5.3. 验证 5.3.1. 什么是Spring Security的验证呢? 5.3.2. 直接设置SecurityContextHolder的内容 5.4. 在web应用中验证 5.4.1. ExceptionTranslationFilter 5.4.2. AuthenticationEntryPoint 5.4.3. 验证机制 5.4.4. 在请求之间保存SecurityContext。 5.5. Spring Security中的访问控制(验证) 5.5.1. 安全和AOP建议 5.5.2. 安全对象和AbstractSecurityInterceptor 5.5.2.1. 配置属性是什么? 5.5.2.2. RunAsManager 5.5.2.3. AfterInvocationManager 5.5.2.4. 扩展安全对象模型 5.6. 国际化 6. 核心服务 6.1. The AuthenticationManager, ProviderManager 和 AuthenticationProviders 6.1.1. DaoAuthenticationProvider 6.2. UserDetailsService实现 6.2.1. 内存认证 6.2.2. JdbcDaoImpl 6.2.2.1. 权限分组 6.3. 密码加密 6.3.1. 什么是散列加密? 6.3.2. 为散列加点儿盐 6.3.3. 散列和认证 III. web应用安全 7. 安全过滤器链 7.1. DelegatingFilterProxy 7.2. FilterChainProxy 7.2.1. 绕过过滤器链 7.3. 过滤器顺序 7.4. 使用其他过滤器 —— 基于框架 8. 核心安全过滤器 8.1. FilterSecurityInterceptor 8.2. ExceptionTranslationFilter 8.2.1. AuthenticationEntryPoint 8.2.2. AccessDeniedHandler 8.3. SecurityContextPersistenceFilter 8.3.1. SecurityContextRepository 8.4. UsernamePasswordAuthenticationFilter 8.4.1. 认证成功和失败的应用流程 9. Basic(基本)和Digest(摘要)验证 9.1. BasicAuthenticationFilter 9.1.1. 配置 9.2. DigestAuthenticationFilter 9.2.1. Configuration 10. Remember-Me认证 10.1. 概述 10.2. 简单基于散列标记的方法 10.3. 持久化标记方法 10.4. Remember-Me接口和实现 10.4.1. TokenBasedRememberMeServices 10.4.2. PersistentTokenBasedRememberMeServices 11. 会话管理 11.1. SessionManagementFilter 11.2. SessionAuthenticationStrategy 11.3. 同步会话 12. 匿名认证 12.1. 概述 12.2. 配置 12.3. AuthenticationTrustResolver IV. 授权 13. 验证架构 13.1. 验证 13.2. 处理预调用 13.2.1. AccessDecisionManager 13.2.2. 基于投票的AccessDecisionManager实现 13.2.2.1. RoleVoter 13.2.2.2. AuthenticatedVoter 13.2.2.3. Custom Voters 13.3. 处理后决定 14. 安全对象实现 14.1. AOP联盟 (MethodInvocation) 安全拦截器 14.1.1. 精确的 MethodSecurityIterceptor 配置 14.2. AspectJ (JoinPoint) 安全拦截器 15. 基于达式的权限控制 15.1. 概述 15.1.1. 常用内建达式 15.2. Web 安全达式 15.3. 方法安全达式 15.3.1. @Pre 和 @Post 注解 15.3.1.1. 访问控制使用 @PreAuthorize 和 @PostAuthorize 15.3.1.2. 过滤使用 @PreFilter 和 @PostFilter V. 高级话题 16. 领域对象安全(ACLs) 16.1. 概述 16.2. 关键概念 16.3. 开始 17. 预认证场景 17.1. 预认证框架类 17.1.1. AbstractPreAuthenticatedProcessingFilter 17.1.2. AbstractPreAuthenticatedAuthenticationDetailsSource 17.1.2.1. J2eeBasedPreAuthenticatedWebAuthenticationDetailsSource 17.1.3. PreAuthenticatedAuthenticationProvider 17.1.4. Http403ForbiddenEntryPoint 17.2. 具体实现 17.2.1. 请求头认证(Siteminder) 17.2.1.1. Siteminder示例配置 17.2.2. J2EE容器认证 18. LDAP认证 18.1. 综述 18.2. 在Spring Security里使用LDAP 18.3. 配置LDAP服务器 18.3.1. 使用嵌入测试服务器 18.3.2. 使用绑定认证 18.3.3. 读取授权 18.4. 实现类 18.4.1. LdapAuthenticator实现 18.4.1.1. 常用功能 18.4.1.2. BindAuthenticator 18.4.1.3. PasswordComparisonAuthenticator 18.4.1.4. 活动目录认证 18.4.2. 链接到LDAP服务器 18.4.3. LDAP搜索对象 18.4.3.1. FilterBasedLdapUserSearch 18.4.4. LdapAuthoritiesPopulator 18.4.5. Spring Bean配置 18.4.6. LDAP属性和自定义UserDetails 19. JSP标签库 19.1. 声明Taglib 19.2. authorize标签 19.3. authentication 标签 19.4. accesscontrollist 标签 20. Java认证和授权服务(JAAS)供应器 20.1. 概述 20.2. 配置 20.2.1. JAAS CallbackHandler 20.2.2. JAAS AuthorityGranter 21. CAS认证 21.1. 概述 21.2. CAS是如何工作的 21.3. 配置CAS客户端 22. X.509认证 22.1. 概述 22.2. 把X.509认证添加到你的web系统中 22.3. 为tomcat配置SSL 23. 替换验证身份 23.1. 概述 23.2. 配置 A. 安全数据库结构 A.1. User A.1.1. 组权限 A.2. 持久登陆(Remember-Me) A.3. ACL A.3.1. Hypersonic SQL A.3.1.1. PostgreSQL B. 安全命名空间 B.1. Web应用安全 - <http>元素 B.1.1. <http>属性 B.1.1.1. servlet-api-provision B.1.1.2. path-type B.1.1.3. lowercase-comparisons B.1.1.4. realm B.1.1.5. entry-point-ref B.1.1.6. access-decision-manager-ref B.1.1.7. access-denied-page B.1.1.8. once-per-request B.1.1.9. create-session B.1.2. <access-denied-handler> B.1.3. <intercept-url>元素 B.1.3.1. pattern B.1.3.2. method B.1.3.3. access B.1.3.4. requires-channel B.1.3.5. filters B.1.4. <port-mappings>元素 B.1.5. <form-login>元素 B.1.5.1. login-page B.1.5.2. login-processing-url B.1.5.3. default-target-url B.1.5.4. always-use-default-target B.1.5.5. authentication-failure-url B.1.5.6. authentication-success-handler-ref B.1.5.7. authentication-failure-handler-ref B.1.6. <http-basic>元素 B.1.7. <remember-me>元素 B.1.7.1. data-source-ref B.1.7.2. token-repository-ref B.1.7.3. services-ref B.1.7.4. token-repository-ref B.1.7.5. key属性 B.1.7.6. token-validity-seconds B.1.7.7. user-service-ref B.1.8. <session-management> 元素 B.1.8.1. session-fixation-protection B.1.9. <concurrent-control>元素 B.1.9.1. max-sessions属性 B.1.9.2. expired-url属性 B.1.9.3. error-if-maximum-exceeded属性 B.1.9.4. session-registry-alias和session-registry-ref属性 B.1.10. <anonymous>元素 B.1.11. <x509>元素 B.1.11.1. subject-principal-regex属性 B.1.11.2. user-service-ref属性 B.1.12. <openid-login>元素 B.1.13. <logout>元素 B.1.13.1. logout-url属性 B.1.13.2. logout-success-url属性 B.1.13.3. invalidate-session属性 B.1.14. <custom-filter>元素 B.2. 认证服务 B.2.1. <authentication-manager>元素 B.2.1.1. <authentication-provider>元素 B.2.1.2. 使用 <authentication-provider> 来引用一个 AuthenticationProvider Bean B.3. 方法安全 B.3.1. <global-method-security>元素 B.3.1.1. secured-annotations和jsr250-annotations属性 B.3.1.2. 安全方法使用<protect-pointcut> B.3.1.3. <after-invocation-provider> 元素 B.3.2. LDAP命名空间选项 B.3.2.1. 使用<ldap-server>元素定义LDAP服务器 B.3.2.2. <ldap-provider>元素 B.3.2.3. <ldap-user-service>元素
Spring Security是一个开源框架,提供了一套可扩展的身份验证和授权机制,可以用于保护Web应用程序中的资源。Spring Security并不强制要求使用特定的数据库结构,但是它提供了一些默认的结构,可以通过配置进行修改。 默认情况下,Spring Security使用以下: 1. users:存储用户信息,包括用户名、密码等。 2. authorities:存储用户的角色信息。 3. groups:存储用户组信息。 4. group_authorities:存储用户组和角色之间的关系。 5. group_members:存储用户和用户组之间的关系。 6. persistent_logins:存储自动登录信息。 下面是一个示例的Spring Security结构设计: CREATE TABLE users ( username VARCHAR(50) NOT NULL, password VARCHAR(100) NOT NULL, enabled TINYINT NOT NULL DEFAULT 1, PRIMARY KEY (username) ); CREATE TABLE authorities ( username VARCHAR(50) NOT NULL, authority VARCHAR(50) NOT NULL, PRIMARY KEY (username, authority) ); CREATE TABLE groups ( id BIGINT NOT NULL AUTO_INCREMENT, name VARCHAR(50) NOT NULL, PRIMARY KEY (id), UNIQUE KEY ix_groups_name (name) ); CREATE TABLE group_authorities ( group_id BIGINT NOT NULL, authority VARCHAR(50) NOT NULL, PRIMARY KEY (group_id, authority), CONSTRAINT fk_group_authorities_groups FOREIGN KEY (group_id) REFERENCES groups(id) ); CREATE TABLE group_members ( id BIGINT NOT NULL AUTO_INCREMENT, username VARCHAR(50) NOT NULL, group_id BIGINT NOT NULL, PRIMARY KEY (id), UNIQUE KEY ix_group_members (username, group_id), CONSTRAINT fk_group_members_users FOREIGN KEY (username) REFERENCES users(username), CONSTRAINT fk_group_members_groups FOREIGN KEY (group_id) REFERENCES groups(id) ); CREATE TABLE persistent_logins ( username VARCHAR(64) NOT NULL, series VARCHAR(64) NOT NULL, token VARCHAR(64) NOT NULL, last_used TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (series) ); 这个示例中,users存储用户信息,包括用户名、密码和是否启用。authorities存储用户的角色信息。groups存储用户组信息,包括名称。group_authorities存储用户组和角色之间的关系。group_members存储用户和用户组之间的关系。persistent_logins存储自动登录信息,包括用户名、系列号、令牌和最后使用时间。 当然,这只是一个示例的结构设计,具体的实现需要根据具体的需求进行设计

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值