• springboot实现ACL+RBAC权限体系


    本文基于web系统的权限控制非常重要的前提下,从ALC和RBAC权限控制两个方面,介绍如何在springboot项目中实现一个完整的权限体系。

    源码下载 :https://gitee.com/skyblue0678/springboot-demo

    序章

    一个后台管理系统,基本都有一套自己的权限体系,权限体系分两种分别是基于控制列表的权限,和基于角色的权限。

    基于控制列表的权限,也叫ACL认证体系。就是每个权限,你可以理解为每一个controller方法都有一个自己的用户列表,只有存在于该列表的用户可以访问这个方法。

    而基于角色的权限,就是每个用户有自己的角色,而角色拥有多个controller方法的访问权限,用户和角色,角色和接口都是多对多的关系。

    ACL: Access Control List

    简介:以前非常盛行的一种权限设计,它的核心主要在于用户和权限直接挂钩。

    优点:简单易用、开发便捷。

    缺点:用户是直接和权限挂钩,导致了在授予权限的时候的复杂性,比较分散,不太易于管理。

    例子:常见的文件系统,直接给用户家权限。比如给用户加读写的权限。

    RBAC:Role Based Access Control

    简介:基于角色的访问控制系统。权限是与角色进行相关联,用户通过成为适当的角色成员从而得到这些角色的权限。

    优点:简化了用户和权限的管理,用过对用户进行分类,使得其与角色和权限关联起来。

    缺点:开发起来相对于ACL复杂。

    例子:基于REAC模型的权限验证框架与应用Apache Shiro、Spring security。

    第一章 ACL的权限控制

    表结构设计

    上面说了,ACL是权限和用户直接挂钩,我们需要设计数据库表来存储用户和权限信息。以下是简化的表结构设计:

    sql:

    1. DROP TABLE IF EXISTS `acl`;
    2. CREATE TABLE `acl` (
    3.   `id` int NOT NULL,
    4.   `user_id` int DEFAULT NULL,
    5.   `permission_id` int DEFAULT NULL,
    6.   PRIMARY KEY (`id`),
    7.   KEY `user_id` (`user_id`),
    8.   KEY `permission_id` (`permission_id`)
    9. ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
    10. DROP TABLE IF EXISTS `permission`;
    11. CREATE TABLE `permission` (
    12.   `id` int NOT NULL AUTO_INCREMENT,
    13.   `permission` varchar(50DEFAULT NULL,
    14.   PRIMARY KEY (`id`)
    15. ) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
    16. DROP TABLE IF EXISTS `user`;
    17. CREATE TABLE `user` (
    18.   `id` int NOT NULL AUTO_INCREMENT,
    19.   `username` varchar(50DEFAULT NULL,
    20.   `password` varchar(50DEFAULT NULL,
    21.   PRIMARY KEY (`id`)
    22. ) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

    初始化数据:

    1. -- 插入用户数据  
    2. INSERT INTO User (id, username, password) VALUES (1'jack''1');  
    3. INSERT INTO User (id, username, password) VALUES (2'rose''1');  
    4.   
    5. -- 插入权限数据  
    6. INSERT INTO Permission (id, permission) VALUES (1'device:list');  
    7. INSERT INTO Permission (id, permission) VALUES (2'device:add');  
    8.   
    9. -- 插入ACL数据  
    10. INSERT INTO ACL (id, user_id, permission_id) VALUES (111);  
    11. INSERT INTO ACL (id, user_id, permission_id) VALUES (222);

    ACL权限设计代码

    有了用户,权限,acl三张表后,就根据matisplus将对应的增删改查功能实现。

    Mapper

    1. public interface UserMapper extends BaseMapper<User> {
    2. }
    3. public interface PermissionMapper extends BaseMapper<Permission> {
    4. }
    5. public interface AclMapper extends BaseMapper<Acl> {
    6. }

    Service

    1. public interface UserService  extends IService<User> {
    2.     User getByUsername(String username);
    3. }
    4. public interface PermissionService extends IService<Permission> {
    5.     List<Permission> findByIds(Set<Integer> permIds);
    6. }
    7. public interface AclService extends IService<Acl> {
    8.     List<Permission> findByUserId(Integer id);
    9. }

    impl

    1. @Service
    2. @Slf4j
    3. public class UserServiceImpl  extends ServiceImpl<UserMapper, User> implements UserService {
    4.     @Override
    5.     public User getByUsername(String username) {
    6.         return baseMapper.selectOne(new LambdaQueryWrapper<User>().eq(User::getUserName,username));
    7.     }
    8. }
    9. @Service
    10. @Slf4j
    11. public class PermissionServiceImpl extends ServiceImpl<PermissionMapper, Permission> implements PermissionService {
    12.     @Override
    13.     public List<Permission> findByIds(Set<Integer> permIds) {
    14.         return baseMapper.selectBatchIds(permIds);
    15.     }
    16. }
    17. @Service
    18. @Slf4j
    19. public class AclServiceImpl extends ServiceImpl<AclMapper, Acl> implements AclService {
    20.     @Resource
    21.     PermissionService permissionService;
    22.     @Override
    23.     public List<Permission> findByUserId(Integer id) {
    24.         List<Acl> acls = baseMapper.selectList(new LambdaQueryWrapper<Acl>().eq(Acl::getUserId, id));
    25.         Set<Integer> permIds = acls.stream().map(Acl::getPermissionId).collect(Collectors.toSet());
    26.         return permissionService.findByIds(permIds);
    27.     }
    28. }

    最后是在login里面获取用户的权限

    1. @GetMapping("/acl/login")
    2. public String loginAcl(String username,String password){
    3.     User user = userService.getByUsername(username);
    4.     if(Objects.nonNull(user)){
    5.         if(!user.getPwd().equals(password)){
    6.             return "密码错误!";
    7.         }
    8.         //根据用户获取ACL权限列表
    9.         List<Permission> permissionList = aclService.findByUserId(user.getId());
    10.         return permissionList.toString();
    11.     }else {
    12.         return "用户名不存在";
    13.     }
    14. }

    测试:

    浏览器访问: http://localhost:8080/acl/login?username=jack&password=1

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

    这个例子中,我们通过用户登录,获取了对应的权限列表,后续的章节中,我们会做真实的权限校验。

    第二章 RBAC的权限控制

    RBAC比ACL就是多了一个角色的概念,用户是拥有角色的,角色对应具体的权限,所以控制起来更加的灵活。

    表结构设计

    以下是简化的表结构设计:

    sql

    1. DROP TABLE IF EXISTS `role`;
    2. CREATE TABLE `role` (
    3.   `id` int NOT NULL AUTO_INCREMENT,
    4.   `role_name` varchar(20DEFAULT NULL,
    5.   PRIMARY KEY (`id`)
    6. ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
    7. INSERT INTO `role` VALUES ('1''设备管理员');
    8. DROP TABLE IF EXISTS `role_permission`;
    9. CREATE TABLE `role_permission` (
    10.   `id` int NOT NULL AUTO_INCREMENT,
    11.   `role_id` int DEFAULT NULL,
    12.   `permission_id` int DEFAULT NULL,
    13.   PRIMARY KEY (`id`)
    14. ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
    15. INSERT INTO `role_permission` VALUES ('1''1''1');
    16. DROP TABLE IF EXISTS `user_role`;
    17. CREATE TABLE `user_role` (
    18.   `id` int NOT NULL AUTO_INCREMENT,
    19.   `user_id` int DEFAULT NULL,
    20.   `role_id` int DEFAULT NULL,
    21.   PRIMARY KEY (`id`)
    22. ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
    23. INSERT INTO `user_role` VALUES ('1''1''1');

    RBAC的登录方法

    1. @GetMapping("/rbac/login")
    2. public String loginRbac(String username,String password){
    3.     User user = userService.getByUsername(username);
    4.     if(Objects.nonNull(user)){
    5.         if(!user.getPwd().equals(password)){
    6.             return "密码错误!";
    7.         }
    8.         //根据用户获取ACL权限列表
    9.         List<Permission> permissionList = userService.findByUserRole(user.getId());
    10.         return permissionList.toString();
    11.     }else {
    12.         return "用户名不存在";
    13.     }
    14. }

    根据用户角色查询权限

    1. @Override
    2. public List<PermissionfindByUserRole(Integer id) {
    3.     return baseMapper.findByUserRole(id);
    4. }

    mapper

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

    xml:

    1. <?xml version="1.0" encoding="UTF-8"?>
    2. <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    3. <mapper namespace="com.it.demo.mapper.UserMapper">
    4.     <select id="findByUserRole" resultType="com.it.demo.entity.Permission">
    5.         SELECT
    6.             e.*
    7.         FROM
    8.             USER a
    9.                 LEFT JOIN user_role b ON a.id = b.user_id
    10.                 LEFT JOIN role c ON b.role_id = c.id
    11.                 LEFT JOIN role_permission d ON c.id = d.role_id
    12.                 LEFT JOIN permission e ON d.permission_id = e.id
    13.         WHERE
    14.             a.id = #{id}
    15.     </select>
    16. </mapper>

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

    [Permission(id=1, permission=device:list)]
    
  • 相关阅读:
    ElementPlus主题色修改
    PyG搭建GCN实现链接预测
    【算法】【递归与动态规划模块】换钱的最小硬币数
    习题2.17
    35道Rust面试题
    【无标题】
    http 内容协商
    为什么你应该聘请前端开发人员
    一个是证书服务和web安全访问配置,一个是PGP的使用
    centos7多主机--实现时间同步chrony服务
  • 原文地址:https://blog.csdn.net/weixin_39570751/article/details/133386557