泽兴芝士网

一站式 IT 编程学习资源平台

基于SpringBoot实现权限控制:ACL与RBAC详解

在Web系统中,权限控制是确保系统安全性和数据完整性的重要组成部分。本文将详细介绍如何在SpringBoot项目中实现两种常见的权限控制模型:ACL(Access Control List)和RBAC(Role-Based Access Control)。通过本文,你将了解这两种权限控制模型的设计思路、表结构设计、代码实现以及如何在SpringBoot项目中应用它们。

序章:权限控制的基本概念

在后台管理系统中,权限控制是必不可少的。常见的权限控制模型有两种:

  1. ACL(Access Control List):基于控制列表的权限控制。每个权限(如Controller方法)都有一个用户列表,只有列表中的用户才能访问该权限。
  2. RBAC(Role-Based Access Control):基于角色的权限控制。用户通过角色与权限关联,角色拥有多个权限,用户通过成为角色的成员来获得权限。

ACL与RBAC的对比

特性

ACL(访问控制列表)

RBAC(基于角色的访问控制)

核心思想

用户直接与权限挂钩

用户通过角色与权限关联

优点

简单易用,开发便捷

简化用户和权限管理,易于扩展

缺点

权限管理分散,用户直接关联权限,管理复杂

开发相对复杂,需要设计角色和权限的关联

适用场景

简单的权限控制场景,如文件系统的读写权限

复杂的权限控制场景,如企业级管理系统

接下来,我们将分别介绍如何在SpringBoot项目中实现ACL和RBAC权限控制。


第一章:ACL权限控制

1.1 表结构设计

ACL的核心是用户直接与权限挂钩,因此我们需要设计三张表:

  1. 用户表(User):存储用户信息。
  2. 权限表(Permission):存储权限信息。
  3. ACL表(ACL):存储用户与权限的关联关系。

表结构SQL

-- 用户表
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
  `id` int NOT NULL AUTO_INCREMENT,
  `username` varchar(50) DEFAULT NULL,
  `password` varchar(50) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

-- 权限表
DROP TABLE IF EXISTS `permission`;
CREATE TABLE `permission` (
  `id` int NOT NULL AUTO_INCREMENT,
  `permission` varchar(50) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

-- ACL表
DROP TABLE IF EXISTS `acl`;
CREATE TABLE `acl` (
  `id` int NOT NULL,
  `user_id` int DEFAULT NULL,
  `permission_id` int DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `user_id` (`user_id`),
  KEY `permission_id` (`permission_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

初始化数据

-- 插入用户数据
INSERT INTO `user` (id, username, password) VALUES (1, 'jack', '1');
INSERT INTO `user` (id, username, password) VALUES (2, 'rose', '1');

-- 插入权限数据
INSERT INTO `permission` (id, permission) VALUES (1, 'device:list');
INSERT INTO `permission` (id, permission) VALUES (2, 'device:add');

-- 插入ACL数据
INSERT INTO `acl` (id, user_id, permission_id) VALUES (1, 1, 1);
INSERT INTO `acl` (id, user_id, permission_id) VALUES (2, 2, 2);

1.2 ACL权限设计的代码实现

1.2.1 Mapper层

使用MyBatis-Plus实现基本的CRUD操作。

public interface UserMapper extends BaseMapper<User> {
}

public interface PermissionMapper extends BaseMapper<Permission> {
}

public interface AclMapper extends BaseMapper<Acl> {
}

1.2.2 Service层

定义Service接口及其实现类。

public interface UserService extends IService<User> {
    User getByUsername(String username);
}

public interface PermissionService extends IService<Permission> {
    List<Permission> findByIds(Set<Integer> permIds);
}

public interface AclService extends IService<Acl> {
    List<Permission> findByUserId(Integer id);
}

1.2.3 Service实现类

@Service
@Slf4j
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
    @Override
    public User getByUsername(String username) {
        return baseMapper.selectOne(new LambdaQueryWrapper<User>().eq(User::getUsername, username));
    }
}

@Service
@Slf4j
public class PermissionServiceImpl extends ServiceImpl<PermissionMapper, Permission> implements PermissionService {
    @Override
    public List<Permission> findByIds(Set<Integer> permIds) {
        return baseMapper.selectBatchIds(permIds);
    }
}

@Service
@Slf4j
public class AclServiceImpl extends ServiceImpl<AclMapper, Acl> implements AclService {
    @Resource
    private PermissionService permissionService;

    @Override
    public List<Permission> findByUserId(Integer id) {
        List<Acl> acls = baseMapper.selectList(new LambdaQueryWrapper<Acl>().eq(Acl::getUserId, id));
        Set<Integer> permIds = acls.stream().map(Acl::getPermissionId).collect(Collectors.toSet());
        return permissionService.findByIds(permIds);
    }
}

1.2.4 登录接口

通过登录接口获取用户的权限列表。

@GetMapping("/acl/login")
public String loginAcl(String username, String password) {
    User user = userService.getByUsername(username);
    if (Objects.nonNull(user)) {
        if (!user.getPassword().equals(password)) {
            return "密码错误!";
        }
        // 根据用户获取ACL权限列表
        List<Permission> permissionList = aclService.findByUserId(user.getId());
        return permissionList.toString();
    } else {
        return "用户名不存在";
    }
}

1.2.5 测试

访问以下URL,测试ACL权限控制:

http://localhost:8080/acl/login?username=jack&password=1

返回结果:

[Permission(id=1, permission=device:list)]

第二章:RBAC权限控制

2.1 表结构设计

RBAC的核心是用户通过角色与权限关联,因此我们需要设计四张表:

  1. 用户表(User):存储用户信息。
  2. 角色表(Role):存储角色信息。
  3. 权限表(Permission):存储权限信息。
  4. 用户角色表(UserRole):存储用户与角色的关联关系。
  5. 角色权限表(RolePermission):存储角色与权限的关联关系。

表结构SQL

-- 角色表
DROP TABLE IF EXISTS `role`;
CREATE TABLE `role` (
  `id` int NOT NULL AUTO_INCREMENT,
  `role_name` varchar(20) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

-- 角色权限表
DROP TABLE IF EXISTS `role_permission`;
CREATE TABLE `role_permission` (
  `id` int NOT NULL AUTO_INCREMENT,
  `role_id` int DEFAULT NULL,
  `permission_id` int DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

-- 用户角色表
DROP TABLE IF EXISTS `user_role`;
CREATE TABLE `user_role` (
  `id` int NOT NULL AUTO_INCREMENT,
  `user_id` int DEFAULT NULL,
  `role_id` int DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

初始化数据

-- 插入角色数据
INSERT INTO `role` VALUES (1, '设备管理员');

-- 插入角色权限数据
INSERT INTO `role_permission` VALUES (1, 1, 1);

-- 插入用户角色数据
INSERT INTO `user_role` VALUES (1, 1, 1);

2.2 RBAC权限设计的代码实现

2.2.1 Mapper层

public interface UserMapper extends BaseMapper<User> {
    List<Permission> findByUserRole(@Param("id") Integer id);
}

2.2.2 Service层

public interface UserService extends IService<User> {
    List<Permission> findByUserRole(Integer id);
}

2.2.3 Service实现类

@Service
@Slf4j
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
    @Override
    public List<Permission> findByUserRole(Integer id) {
        return baseMapper.findByUserRole(id);
    }
}

2.2.4 XML映射文件

<select id="findByUserRole" resultType="com.example.entity.Permission">
    SELECT e.*
    FROM user a
    LEFT JOIN user_role b ON a.id = b.user_id
    LEFT JOIN role c ON b.role_id = c.id
    LEFT JOIN role_permission d ON c.id = d.role_id
    LEFT JOIN permission e ON d.permission_id = e.id
    WHERE a.id = #{id}
</select>

2.2.5 登录接口

@GetMapping("/rbac/login")
public String loginRbac(String username, String password) {
    User user = userService.getByUsername(username);
    if (Objects.nonNull(user)) {
        if (!user.getPassword().equals(password)) {
            return "密码错误!";
        }
        // 根据用户获取RBAC权限列表
        List<Permission> permissionList = userService.findByUserRole(user.getId());
        return permissionList.toString();
    } else {
        return "用户名不存在";
    }
}

2.2.6 测试

访问以下URL,测试RBAC权限控制:

http://localhost:8080/rbac/login?username=jack&password=1

返回结果:

[Permission(id=1, permission=device:list)]

总结

ACL适用于简单的权限控制场景,而RBAC更适合复杂的权限管理需求。在实际项目中,可以根据业务需求选择合适的权限控制模型,或者将两者结合使用,以实现更灵活的权限管理。

控制面板
您好,欢迎到访网站!
  查看权限
网站分类
最新留言