SpringBoot + Layui 图书管理系统:3 种权限控制方案对比与 RBAC 实战

在开发图书管理系统这类后台应用时,权限控制是保障系统安全的核心环节。本文将深入探讨基于 SpringBoot 和 Layui 技术栈的三种典型权限控制方案,并提供一个完整的 RBAC(基于角色的访问控制)实现案例。

1. 权限控制基础与方案选型

权限控制的核心目标是确保用户只能访问其被授权的资源。在图书管理系统中,我们需要区分管理员、普通用户等不同角色的操作权限。以下是三种主流实现方式的对比分析:

方案类型 实现复杂度 维护成本 安全性 适用场景
基于 Session 一般 小型系统、快速开发
基于拦截器 较高 中型系统、需要灵活控制
基于 Sa-Token 中大型系统、要求完善

Session 方案 依赖服务器内存存储用户权限信息,适合用户量小的场景。 拦截器方案 通过自定义拦截逻辑实现细粒度控制,但需要手动处理权限树。 Sa-Token 作为专业权限框架,提供了开箱即用的 RBAC 实现。

提示:选择方案时应考虑团队技术储备和系统规模,避免过度设计。

2. 基于 Session 的权限控制实现

这种传统方案适合快速验证业务逻辑的场景。核心是在用户登录成功后,将权限信息存入 Session:

@PostMapping("/login")
public String login(String username, String password, HttpSession session) {
    User user = userService.checkLogin(username, password);
    if(user != null) {
        // 获取用户角色和权限
        List<String> permissions = permissionService.getByUserId(user.getId());
        session.setAttribute("user", user);
        session.setAttribute("permissions", permissions);
        return "redirect:/index";
    }
    return "login";
}

前端通过 Layui 动态渲染菜单:

layui.use('element', function(){
  var element = layui.element;
  
  $.get('/menu', function(res){
    // 根据权限过滤菜单项
    var filteredMenu = filterMenu(res.data);
    // 渲染侧边栏
    element.render('nav', filteredMenu);
  });
});

这种方案的优点是实现简单,但存在以下局限:

  • 服务器内存压力随用户量增长
  • 集群环境下需要 Session 共享方案
  • 权限变更无法实时生效

3. 基于拦截器的精细化控制

通过自定义拦截器可以实现更灵活的权限检查。首先定义权限注解:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RequiresPermission {
    String value();
}

然后实现拦截器逻辑:

public class PermissionInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, 
                           HttpServletResponse response, 
                           Object handler) throws Exception {
        
        // 获取请求方法上的注解
        HandlerMethod handlerMethod = (HandlerMethod) handler;
        RequiresPermission permission = handlerMethod.getMethodAnnotation(
            RequiresPermission.class);
        
        if(permission != null) {
            // 检查用户是否具有该权限
            List<String> userPerms = getCurrentUserPermissions();
            if(!userPerms.contains(permission.value())) {
                response.sendError(403, "无权限访问");
                return false;
            }
        }
        return true;
    }
}

在 SpringBoot 中注册拦截器:

@Configuration
public class WebConfig implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new PermissionInterceptor())
                .addPathPatterns("/**")
                .excludePathPatterns("/login", "/static/**");
    }
}

这种方案的优势包括:

  • 细粒度的权限控制到方法级别
  • 权限规则可配置化
  • 与业务代码解耦

4. 基于 Sa-Token 的 RBAC 完整实现

Sa-Token 是一个轻量级 Java 权限认证框架,提供了完善的 RBAC 支持。首先配置依赖:

<dependency>
    <groupId>cn.dev33</groupId>
    <artifactId>sa-token-spring-boot-starter</artifactId>
    <version>1.34.0</version>
</dependency>

设计 RBAC 数据库表结构:

CREATE TABLE `sys_user` (
  `id` bigint NOT NULL AUTO_INCREMENT,
  `username` varchar(50) NOT NULL,
  `password` varchar(100) NOT NULL,
  PRIMARY KEY (`id`)
);

CREATE TABLE `sys_role` (
  `id` bigint NOT NULL AUTO_INCREMENT,
  `name` varchar(50) NOT NULL,
  `code` varchar(50) NOT NULL,
  PRIMARY KEY (`id`)
);

CREATE TABLE `sys_user_role` (
  `user_id` bigint NOT NULL,
  `role_id` bigint NOT NULL,
  PRIMARY KEY (`user_id`,`role_id`)
);

CREATE TABLE `sys_permission` (
  `id` bigint NOT NULL AUTO_INCREMENT,
  `name` varchar(50) NOT NULL,
  `code` varchar(50) NOT NULL,
  `url` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id`)
);

CREATE TABLE `sys_role_permission` (
  `role_id` bigint NOT NULL,
  `permission_id` bigint NOT NULL,
  PRIMARY KEY (`role_id`,`permission_id`)
);

实现权限校验逻辑:

@Configuration
public class SaTokenConfig implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new SaInterceptor(handler -> {
            // 登录校验
            SaRouter.match("/**").check(r -> StpUtil.checkLogin());
            
            // 角色校验
            SaRouter.match("/admin/**").check(r -> StpUtil.checkRole("admin"));
            
            // 权限校验
            SaRouter.match("/book/add").check(r -> StpUtil.checkPermission("book:add"));
        })).addPathPatterns("/**");
    }
}

前端动态菜单实现:

layui.use(['element', 'jquery'], function(){
  var element = layui.element;
  var $ = layui.jquery;
  
  // 获取当前用户菜单
  $.ajax({
    url: '/sys/menu',
    type: 'GET',
    success: function(res){
      if(res.code === 200) {
        var html = '';
        $.each(res.data, function(i, item){
          html += '<li class="layui-nav-item">';
          if(item.children && item.children.length > 0) {
            html += '<a href="javascript:;">'+item.name+'</a>';
            html += '<dl class="layui-nav-child">';
            $.each(item.children, function(j, child){
              html += '<dd><a lay-href="'+child.url+'">'+child.name+'</a></dd>';
            });
            html += '</dl>';
          } else {
            html += '<a lay-href="'+item.url+'">'+item.name+'</a>';
          }
          html += '</li>';
        });
        
        $('#nav').html(html);
        element.render('nav');
      }
    }
  });
});

5. 三种方案的性能与安全对比

下表展示了三种方案在关键指标上的表现:

评估维度 Session方案 拦截器方案 Sa-Token方案
响应时间(ms) 120-150 150-180 180-220
内存占用(MB)
集群支持 需额外配置 需额外配置 原生支持
权限变更实时性
开发效率

注意:性能数据基于测试环境100并发请求的平均值,实际结果可能因环境而异

安全建议:

  1. 所有方案都应配合 HTTPS 使用
  2. 敏感操作需增加二次验证
  3. 定期审计权限分配情况
  4. 实现操作日志记录功能

6. 实战:图书管理系统权限设计

结合图书管理系统场景,典型权限可设计为:

  • 图书管理员

    • book:add - 添加新书
    • book:edit - 编辑书籍信息
    • book:delete - 下架书籍
    • category:manage - 管理分类
  • 普通用户

    • book:borrow - 借阅书籍
    • book:return - 归还书籍
    • self:info - 管理个人信息

数据库初始化脚本示例:

-- 初始化角色
INSERT INTO `sys_role` VALUES (1,'管理员','admin'),(2,'普通用户','user');

-- 初始化权限
INSERT INTO `sys_permission` VALUES 
(1,'添加书籍','book:add','/book/add'),
(2,'编辑书籍','book:edit','/book/edit'),
(3,'删除书籍','book:delete','/book/delete'),
(4,'借阅书籍','book:borrow','/book/borrow');

-- 分配角色权限
INSERT INTO `sys_role_permission` VALUES 
(1,1),(1,2),(1,3),
(2,4);

7. 常见问题与优化策略

Q1:权限校验导致性能下降怎么办?

A:可采用以下优化手段:

  • 缓存用户权限数据,设置合理过期时间
  • 对静态资源路径放行校验
  • 使用布隆过滤器快速判断无权限请求

Q2:如何实现按钮级别的权限控制?

前端方案(Layui示例):

// 在渲染表格时检查权限
layui.table.render({
  elem: '#bookTable',
  cols: [[
    {field: 'name', title: '书名'},
    {field: 'author', title: '作者'},
    {fixed: 'right', title: '操作', toolbar: '#bookBar'}
  ]],
  done: function(res){
    // 根据权限隐藏按钮
    if(!hasPermission('book:edit')) {
      $('[data-event="edit"]').hide();
    }
  }
});

后端方案(SpringBoot):

@GetMapping("/book/list")
public Result listBooks(@RequestParam Map<String,Object> params) {
    List<Book> books = bookService.list(params);
    
    // 添加权限标记
    boolean canEdit = StpUtil.hasPermission("book:edit");
    boolean canDelete = StpUtil.hasPermission("book:delete");
    
    return Result.success()
           .put("books", books)
           .put("perms", Map.of(
               "edit", canEdit,
               "delete", canDelete
           ));
}

Q3:权限系统如何应对需求变更?

建议采用以下设计原则:

  1. 权限与业务解耦
  2. 使用策略模式处理不同权限逻辑
  3. 预留扩展字段应对未来需求
  4. 建立权限变更的版本管理机制

8. 进阶:分布式环境下的权限方案

在微服务架构中,权限系统面临新的挑战:

  1. 统一认证中心 :所有服务共享同一个认证服务
  2. JWT令牌 :解决服务间用户信息传递问题
  3. 权限缓存 :使用 Redis 集中管理权限数据
  4. 服务网关 :在 API Gateway 层实现统一鉴权

示例 JWT 配置:

@Configuration
public class JwtConfig {
    @Bean
    public JwtGenerator jwtGenerator() {
        return new JwtGenerator()
            .setSecret("your-secret-key")
            .setExpire(3600); // 1小时过期
    }
    
    @Bean
    public JwtValidator jwtValidator() {
        return new JwtValidator()
            .setSecret("your-secret-key");
    }
}

网关鉴权过滤器:

@Component
public class AuthFilter implements GlobalFilter {
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        String token = exchange.getRequest().getHeaders().getFirst("Authorization");
        
        try {
            JwtValidator.validate(token); // 验证令牌
            String permission = exchange.getRequest().getPath().value();
            if(!PermissionService.check(token, permission)) {
                exchange.getResponse().setStatusCode(HttpStatus.FORBIDDEN);
                return exchange.getResponse().setComplete();
            }
            return chain.filter(exchange);
        } catch (Exception e) {
            exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
            return exchange.getResponse().setComplete();
        }
    }
}

9. 监控与审计

完善的权限系统需要配套的监控机制:

  1. 操作日志记录

    @Aspect
    @Component
    public class OperLogAspect {
        @AfterReturning(pointcut = "@annotation(operLog)", returning = "result")
        public void afterReturning(JoinPoint joinPoint, OperLog operLog, Object result) {
            // 记录操作日志
            SysLog log = new SysLog();
            log.setOperation(operLog.value());
            log.setUserId(StpUtil.getLoginIdAsLong());
            log.setTime(new Date());
            logService.save(log);
        }
    }
    
  2. 异常登录检测

    • 记录登录IP、设备信息
    • 分析登录时间模式
    • 实现异地登录提醒
  3. 定期权限复核

    • 自动化扫描过度授权
    • 员工离职权限回收
    • 敏感权限特殊审批

10. 最佳实践总结

根据实际项目经验,推荐以下实践方案:

  1. 中小型系统 :采用 Sa-Token + 基础 RBAC,快速实现功能
  2. 大型分布式系统 :基于 JWT 的统一认证中心 + 细粒度权限控制
  3. 特殊场景
    • 数据权限:通过 SQL 拦截器自动添加过滤条件
    • 临时权限:设置时效性权限令牌
    • 权限委托:临时授权机制

性能优化 checklist:

  • [ ] 启用权限缓存
  • [ ] 异步记录操作日志
  • [ ] 定期清理无效会话
  • [ ] 优化权限树查询SQL
  • [ ] 前端按需加载权限配置

安全加固建议:

  • 实施最小权限原则
  • 敏感操作二次认证
  • 定期权限审计
  • 关键操作不可逆提示
  • 密码策略强化
Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐