尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

权限管理系统完整源码Java+MySql+SSM:从建表到AOP鉴权实战

权限管理系统完整源码Java+MySql+SSM:从建表到AOP鉴权实战 简介这是一套基于JavaMySQLSSM框架的权限管理系统完整源码面向计算机专业学生、Java初学者及需要完成毕设或课程设计的开发者帮助解决角色、菜单与权限动态分配的管理需求。系统采用B/S体系结构与标准MVC模式划分为表现层、controller层、service层和dao层以JSP作为前端页面MySQL管理数据。核心亮点在于以角色为表头、菜单为首列动态加载权限通过表格内的权限开关实现在线分配免去页面跳转同时以树形结构呈现角色与菜单整合增删改操作并支持菜单图标与按钮管理。压缩包共602个文件约31.44MB包含89个java源文件、24个jsp页面、115个js脚本、24个css样式、29个xml配置、65个jar依赖及1个sql建库脚本另附class编译文件与图片资源结构完整可直接导入运行。目前已有117人学习下载适合作为SSM框架实战与权限模块设计的参考案例。1. 权限管理系统完整源码JavaMySqlSSM一套能跑通的后台骨架长什么样很多 Java 开发工程师面试题里都会问「用户、角色、权限三张表怎么设计」但真到手上要落地一套权限管理系统完整源码 JavaMySqlSSM 的时候才发现难点根本不在建表而在 SSM 三层怎么把认证、鉴权、菜单渲染串成一条线。这套组合的价值在于它不依赖 Spring Boot 的自动装配魔法所有 XML 配置、拦截器、AOP 切面都摊在明面上你能清楚看到一次请求从 DispatcherServlet 进来经过 HandlerInterceptor 校验 session再到 Service 层查角色权限最后 MyBatis 把菜单树拼出来的全过程。适合谁适合刚学完 SSM 想找个真实项目练手的人也适合接手老系统、需要给一套遗留后台补权限模块的工程师。下面按「先立骨架、再填血肉、最后排雷」的顺序讲透。2. 从零搭 SSM 骨架依赖、数据源与三张核心表2.1 Maven 依赖与版本对齐SSM 最烦的就是版本冲突Spring 5.x 配 MyBatis 3.5.x 是相对稳的组合。下面这份 pom 片段是我一般会用的基线注意 spring-jdbc、mybatis-spring 这两个桥接包必须版本匹配否则启动就报 NoClassDefFoundError。!-- pom.xml 关键依赖版本按需微调 -- properties spring.version5.3.30/spring.version mybatis.version3.5.13/mybatis.version /properties dependencies !-- Spring 核心context 管 Beanwebmvc 管 MVCtx 管事务 -- dependency groupIdorg.springframework/groupId artifactIdspring-context/artifactId version${spring.version}/version /dependency dependency groupIdorg.springframework/groupId artifactIdspring-webmvc/artifactId version${spring.version}/version /dependency dependency groupIdorg.springframework/groupId artifactIdspring-jdbc/artifactId version${spring.version}/version /dependency !-- MyBatis 与 Spring 的粘合层版本必须和 mybatis 主包对齐 -- dependency groupIdorg.mybatis/groupId artifactIdmybatis/artifactId version${mybatis.version}/version /dependency dependency groupIdorg.mybatis/groupId artifactIdmybatis-spring/artifactId version2.1.2/version /dependency !-- MySQL 驱动8.x 用 com.mysql.cj.jdbc.Driver -- dependency groupIdmysql/groupId artifactIdmysql-connector-java/artifactId version8.0.33/version /dependency /dependencies逻辑说明spring-webmvc 负责 DispatcherServlet 和注解驱动spring-jdbc 提供 DataSourceTransactionManagermybatis-spring 的 SqlSessionFactoryBean 把 MyBatis 塞进 Spring 容器。参数上MySQL 8 的驱动类名是com.mysql.cj.jdbc.DriverURL 要带serverTimezoneAsia/Shanghai否则时区报错是新手第一个翻车点。2.2 数据源与 MyBatis 配置数据源我倾向用 Druid连接池监控对排查权限查询慢很有用。下面这段是 spring-dao.xml 的核心。!-- spring-dao.xml数据源 SqlSessionFactory 事务 -- bean iddataSource classcom.alibaba.druid.pool.DruidDataSource property namedriverClassName valuecom.mysql.cj.jdbc.Driver/ property nameurl valuejdbc:mysql://localhost:3306/rbac?useUnicodetrueamp;characterEncodingutf8amp;serverTimezoneAsia/Shanghai/ property nameusername valueroot/ property namepassword valueyour_password/ !-- 初始连接数、最大连接数按并发量调后台系统 5/20 够用 -- property nameinitialSize value5/ property namemaxActive value20/ /bean bean idsqlSessionFactory classorg.mybatis.spring.SqlSessionFactoryBean property namedataSource refdataSource/ !-- 别名包让 XML 里 resultType 直接写类名 -- property nametypeAliasesPackage valuecom.example.rbac.entity/ property namemapperLocations valueclasspath:mapper/*.xml/ /bean !-- Mapper 接口扫描省去逐个注册 -- bean classorg.mybatis.spring.mapper.MapperScannerConfigurer property namebasePackage valuecom.example.rbac.mapper/ /bean !-- 事务管理器注解 Transactional 靠它生效 -- bean idtxManager classorg.springframework.jdbc.datasource.DataSourceTransactionManager property namedataSource refdataSource/ /bean tx:annotation-driven transaction-managertxManager/参数说明typeAliasesPackage让 XML 里写resultTypeUser而不是全限定名mapperLocations的路径要和 resources 下实际目录一致放错位置会报Invalid bound statement。事务管理器必须显式声明否则Transactional静默失效这是血泪经验。2.3 三张核心表与菜单表权限系统的表设计绕不开 RBAC。最小可用集合是用户、角色、权限菜单再加两张关联表。表名作用关键字段sys_user用户id, username, password, salt, statussys_role角色id, role_name, role_keysys_menu权限/菜单id, parent_id, menu_name, perms, menu_typesys_user_role用户角色关联user_id, role_idsys_role_menu角色菜单关联role_id, menu_id-- 建库建表字符集统一 utf8mb4 CREATE DATABASE rbac DEFAULT CHARACTER SET utf8mb4; USE rbac; CREATE TABLE sys_user ( id BIGINT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50) NOT NULL UNIQUE, password VARCHAR(100) NOT NULL, salt VARCHAR(32) NOT NULL, status TINYINT DEFAULT 1 COMMENT 1正常 0停用 ); CREATE TABLE sys_menu ( id BIGINT PRIMARY KEY AUTO_INCREMENT, parent_id BIGINT DEFAULT 0, menu_name VARCHAR(50), perms VARCHAR(100) COMMENT 权限标识如 user:add, menu_type CHAR(1) COMMENT M目录 C菜单 F按钮 );逻辑说明perms字段是鉴权的关键Shiro 或自定义拦截器都靠它比对。menu_type区分目录、菜单、按钮前端渲染树时按类型过滤。salt单独存是为了密码加盐别偷懒用固定盐。提示MySQL 8 默认认证插件是 caching_sha2_password老版本客户端连不上时要么升级驱动要么改用户认证方式别急着怀疑代码。3. 认证与鉴权落地拦截器、密码加盐与菜单树3.1 登录认证与密码加盐密码绝不能明文存。常见做法是 MD5 加盐虽然 MD5 已不推荐用于高安全场景但后台系统配合随机盐仍可用。下面是对比逻辑。// 登录时校验密码 public User login(String username, String rawPassword) { User user userMapper.selectByUsername(username); if (user null) { throw new RuntimeException(用户不存在); } // 用库里存的盐再算一次和库里的密文比对 String encoded DigestUtils.md5DigestAsHex( (rawPassword user.getSalt()).getBytes(StandardCharsets.UTF_8)); if (!encoded.equals(user.getPassword())) { throw new RuntimeException(密码错误); } return user; }逻辑说明DigestUtils来自 commons-codecmd5DigestAsHex输出 32 位小写十六进制。参数上盐建议用 UUID 去掉横线长度 32。注意比对时用equals而不是字符串比较踩过坑的都懂。3.2 HandlerInterceptor 做登录与权限校验SSM 里鉴权最直接的方式是拦截器。登录拦截器判断 session 有没有用户权限拦截器判断当前请求的 URL 对应权限是否在用户权限集合里。// 权限拦截器核心逻辑 public class AuthInterceptor implements HandlerInterceptor { Override public boolean preHandle(HttpServletRequest req, HttpServletResponse resp, Object handler) throws Exception { HttpSession session req.getSession(); User user (User) session.getAttribute(loginUser); if (user null) { resp.sendRedirect(req.getContextPath() /login); return false; } // 管理员放行普通用户查权限集合 if (user.isAdmin()) { return true; } String uri req.getRequestURI(); SetString perms (SetString) session.getAttribute(perms); // 简化匹配实际项目建议用 AntPathMatcher if (perms null || !perms.contains(uriToPerm(uri))) { resp.sendError(403, 无权限); return false; } return true; } }逻辑说明preHandle返回 false 就中断请求。参数上uriToPerm是把/user/add映射成user:add的转换函数实际项目里更稳的是用AntPathMatcher做通配匹配。注意拦截器要在 spring-mvc.xml 里注册并排除登录、静态资源路径否则登录页自己都被拦。3.3 菜单树递归拼装前端左侧菜单是树形结构后端要递归拼。用 MyBatis 一次查全部菜单再在 Service 层组装比递归查库高效得多。// 菜单树组装 public ListMenuNode buildMenuTree(ListSysMenu all) { MapLong, MenuNode nodeMap new HashMap(); for (SysMenu m : all) { nodeMap.put(m.getId(), new MenuNode(m)); } ListMenuNode roots new ArrayList(); for (MenuNode node : nodeMap.values()) { if (node.getParentId() 0) { roots.add(node); } else { MenuNode parent nodeMap.get(node.getParentId()); if (parent ! null) { parent.getChildren().add(node); } } } return roots; }逻辑说明先把所有节点放进 Map再一遍遍历挂父子关系时间复杂度 O(n)。参数上parentId为 0 视为根。注意MenuNode的 children 要初始化成空 List否则前端拿到 null 会报错。注意菜单树和权限集合要分开处理。菜单管展示perms 管接口鉴权两者数据源可以同表但用途不同混用会导致按钮权限控制失效。4. 避坑排查SSM 权限系统最常见的 5 个翻车点4.1 中文乱码现象是页面提交后入库变问号现象表单提交中文数据库里存成???。原因字符集链路没统一可能是数据库、连接 URL、Tomcat 任一处。解决数据库建库用 utf8mb4URL 加characterEncodingutf8web.xml 里配CharacterEncodingFilter且forceEncodingtrue三处对齐才彻底。4.2 Invalid bound statementMapper 方法找不到 XML现象启动不报错调用就抛Invalid bound statement (not found)。原因XML 的 namespace 和接口全限定名不一致或 mapperLocations 路径没匹配上。解决检查 XML 的namespace是否等于接口全路径方法 id 是否和接口方法名一致resources 目录结构是否和配置的classpath:mapper/*.xml对得上。4.3 事务不生效Transactional 加了但回滚失败现象Service 里抛异常数据还是写进去了。原因注解加在 private 方法上或同类内部调用绕过代理或没配tx:annotation-driven。解决注解加在 public 方法跨类调用确认事务管理器 Bean 名和transaction-manager一致。4.4 拦截器拦了静态资源CSS/JS 全 404现象登录页样式全丢控制台一堆 404。原因拦截器/**全拦没排除静态资源。解决在 mvc:interceptor 里用mvc:exclude-mapping排除/static/**、/css/**、/js/**或把静态资源交给默认 Servlet 处理。4.5 权限缓存不一致改了角色权限不生效现象后台改了角色菜单用户刷新还是老权限。原因权限存在 session 里改库没同步 session。解决要么改权限后强制用户重新登录要么把权限放 Redis 并设过期session 只存用户 id。小系统用前者别过度设计。5. 进阶技巧用注解 AOP 把权限校验从拦截器里解放出来拦截器按 URL 匹配权限有个硬伤同一个 URL 不同操作没法区分RESTful 风格下更明显。更优雅的做法是自定义注解加 AOP在方法级别声明需要的权限。// 自定义权限注解 Target(ElementType.METHOD) Retention(RetentionPolicy.RUNTIME) public interface RequiresPerm { String value(); // 如 user:add } // AOP 切面校验 Aspect Component public class PermAspect { Around(annotation(requiresPerm)) public Object check(ProceedingJoinPoint pjp, RequiresPerm requiresPerm) throws Throwable { User user (User) ((ServletRequestAttributes) RequestContextHolder .currentRequestAttributes()).getRequest() .getSession().getAttribute(loginUser); if (user null || (!user.isAdmin() !user.getPerms().contains(requiresPerm.value()))) { throw new RuntimeException(无权限); } return pjp.proceed(); } }逻辑说明Around在目标方法前后织入annotation(requiresPerm)绑定注解实例拿到 value。参数上RequestContextHolder能在非 Controller 层拿到当前请求这是关键。注意 AOP 要开启aop:aspectj-autoproxy/且切面类要被 Spring 扫描到。验证方法很简单给一个 Controller 方法加RequiresPerm(user:add)用无权限账号调应该抛异常管理员调则通过。这样权限声明跟着方法走比 URL 匹配直观得多重构时也不容易漏。我自己的习惯是新项目一律用注解 AOP老项目改造才用拦截器兜底。踩过最深的坑是切面里直接 new 对象导致拿不到 session后来统一用 RequestContextHolder 才稳。权限系统这东西表设计错了还能改鉴权入口选错了后期每加一个接口都要动配置那才是真的后悔药没处买。希望帮到你。本文还有配套的精品资源点击获取
返回列表