手写SSM权限管理系统:从数据库表设计到拦截器实现完整解析
2026/9/16 5:27:21 网站建设 项目流程

简介:一套基于Java+MySQL+SSM(Spring+SpringMVC+MyBatis)的权限管理系统完整源码,采用B/S架构与标准MVC分层,适用于毕业设计、SSM框架学习及后台权限模块二次开发。系统以角色为表头、菜单为首列,支持在线分配权限与动态加载角色/菜单/权限,表中直接设置权限开关;同时以树形结构管理角色与菜单,整合增删改、菜单图标及按钮权限配置,交互简洁高效。资源共602个文件,涵盖89个Java源码、24个JSP页面、115个JS脚本、29个XML配置、65个Jar依赖及SQL脚本等,压缩包31.44MB,代码结构清晰,便于导入IDE直接部署调试。核心控制层覆盖用户、角色、菜单、权限管理,并附带验证码工具等通用组件,可直接运行或作为毕设项目参考。已有115人学习下载,适合需要快速搭建权限管理功能或借鉴SSM整合细节的开发者。

1. 权限管理系统:为什么Java+MySQL+SSM这套老组合仍然值得手写一份完整源码

“权限管理系统”在公司后台和毕设项目里出现频率最高,但也是最容易被低估的需求。很多人以为它就是一个登录页加几个if判断,真正拆开看才发现一套能交付的权限管理系统至少包含用户管理、角色管理、菜单管理、按钮权限、操作日志和动态路由,代码量堪比一个小型电商后台。既然Spring Security已经封装好了,为什么还要自己搭一套Java+MySQL+SSM的权限管理系统?原因有三个:老项目技术栈固定,SSM的拦截器机制能让你看清权限校验每一步发生在哪个环节;面试官问到“RBAC怎么落库”“URL越权怎么防”时,只有亲手写过才能答得出细节;这类源码扩展起来特别顺手,数据源换成Druid、缓存换成Redis都不需要推翻重来。本文就按实际项目的组织方式,从数据库、框架配置、登录认证到接口拦截逐一走通。

2. 权限管理系统的MySQL表结构:五张核心表与字段参数

2.1 先定RBAC模型,再写代码

权限管理系统的地基在数据库,数据模型定不下来,后面所有拦截判断都是空中楼阁。业界最通用的是RBAC模型:用户关联角色,角色关联权限,用户通过角色间接获得权限。Spring Security用这套模型,Shiro也用这套模型,自己用SSM实现时同样跑不脱这套设计。RBAC的好处是改权限不用动用户表,新入职一个运营就分配运营角色,离职了把关联关系一删即可,权限变更成本全在角色这一层。

需要注意RBAC的边界:它管不了“某个用户对某个具体订单有单独查看权”这类行级数据权限。做权限管理系统之前要把边界划清楚——菜单权限、按钮权限、接口权限走RBAC,行级数据权限需要另做一套基于规则或基于所有者的过滤机制,不要混在一张表里硬设计。

2.2 建表SQL:用户、角色、权限、关联表四层结构

这五张表是权限管理系统的骨架,建表脚本可以直接拿去用。MySQL 5.7和8.0通用,存储引擎统一InnoDB,字符集统一utf8mb4。

CREATE TABLE `t_user` ( `user_id` int(11) NOT NULL AUTO_INCREMENT COMMENT '用户ID', `username` varchar(50) NOT NULL COMMENT '登录账号', `password` varchar(128) NOT NULL COMMENT '密码(MD5加盐哈希)', `real_name` varchar(20) DEFAULT NULL COMMENT '真实姓名', `status` tinyint(1) DEFAULT '1' COMMENT '状态:1启用 0禁用', `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', `deleted` tinyint(1) DEFAULT '0' COMMENT '逻辑删除:0未删 1已删', PRIMARY KEY (`user_id`), UNIQUE KEY `uk_username` (`username`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户表'; CREATE TABLE `t_role` ( `role_id` int(11) NOT NULL AUTO_INCREMENT COMMENT '角色ID', `role_name` varchar(50) NOT NULL COMMENT '角色名称', `role_code` varchar(50) NOT NULL COMMENT '角色编码', `status` tinyint(1) DEFAULT '1' COMMENT '状态', `create_time` datetime DEFAULT CURRENT_TIMESTAMP, `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, `deleted` tinyint(1) DEFAULT '0', PRIMARY KEY (`role_id`), UNIQUE KEY `uk_role_code` (`role_code`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='角色表'; CREATE TABLE `t_permission` ( `perm_id` int(11) NOT NULL AUTO_INCREMENT COMMENT '权限ID', `perm_name` varchar(50) NOT NULL COMMENT '权限名称', `perm_code` varchar(100) NOT NULL COMMENT '权限编码', `perm_type` tinyint(1) NOT NULL COMMENT '类型:1目录 2菜单 3按钮 4接口', `parent_id` int(11) DEFAULT '0' COMMENT '父节点ID', `url` varchar(200) DEFAULT NULL COMMENT '菜单或接口路径', `icon` varchar(50) DEFAULT NULL COMMENT '图标', `sort_order` int(4) DEFAULT '0' COMMENT '排序', `create_time` datetime DEFAULT CURRENT_TIMESTAMP, `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`perm_id`), KEY `idx_parent_id` (`parent_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='权限表'; CREATE TABLE `t_user_role` ( `id` int(11) NOT NULL AUTO_INCREMENT, `user_id` int(11) NOT NULL COMMENT '用户ID', `role_id` int(11) NOT NULL COMMENT '角色ID', PRIMARY KEY (`id`), UNIQUE KEY `uk_user_role` (`user_id`,`role_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户-角色关联表'; CREATE TABLE `t_role_permission` ( `id` int(11) NOT NULL AUTO_INCREMENT, `role_id` int(11) NOT NULL COMMENT '角色ID', `perm_id` int(11) NOT NULL COMMENT '权限ID', PRIMARY KEY (`id`), UNIQUE KEY `uk_role_perm` (`role_id`,`perm_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='角色-权限关联表';

刚才建表SQL里有几个参数值得单独说明。password字段长度设为128而不是32,是因为MD5加盐后通常是32位十六进制,但后续如果要升级为多重哈希算法或兼容其他加密方式,32位长度会卡死扩展空间。deleted逻辑删除字段是必须的,权限表被角色关联引用,物理删行会导致历史关联数据悬空,账都没法对。两张关联表都建了联合唯一索引,比如uk_user_role,防止重复插入同一对用户和角色,这个索引在批量分配角色时就是唯一约束的兜底。

这里没有写任何物理外键,只用索引和业务代码保证关联完整性。外键在权限管理系统里弊大于利:批量删除、数据迁移、分库分表时全是绊脚石,而且大部分团队规范就是禁用物理外键。日常用Navicat for MySQL打开这五张表时,重点看两张关联表的联合主键是否生效,很多权限错乱问题的根因就是关联表里出现了重复数据。

2.3 权限字典与初始化数据:菜单、按钮、接口都要入库

t_permission表里的perm_type字段是这套系统的核心设计,它把权限粒度分成了四个层级:1目录、2菜单、3按钮、4接口。目录和菜单用于前端动态生成左侧导航,按钮用于页面里的操作按钮显隐,接口用于后端URL级权限匹配。四个类型放在同一张表而不是拆成多张表,是为了让树形结构的查询和排序保持一致,用一个parent_id就能拼出完整权限树。

初始化数据必须植入一条超级管理员角色,常见做法是把角色编码固定为admin,绑定全部权限。用一条关联插入就可以完成初始化,不需要写存储过程:

INSERT INTO t_role (role_name, role_code, status) VALUES ('系统管理员', 'admin', 1); INSERT INTO t_permission (perm_name, perm_code, perm_type, url) VALUES ('用户管理', 'user:manage', 2, '/user/list'), ('新增用户', 'user:add', 3, NULL), ('编辑用户', 'user:edit', 3, NULL), ('删除用户', 'user:delete', 3, NULL), ('查询用户接口', 'user:list:api', 4, '/api/user/list'); INSERT INTO t_role_permission (role_id, perm_id) SELECT 1, perm_id FROM t_permission;

最后一条INSERT ... SELECT是权限管理系统初始化时很常用的小技巧,直接把当前全部权限挂到超级管理员角色上,后面新增权限表数据时再补一条同样的语句即可,不需要手动数着ID去关联。按钮权限把url设为NULL,因为按钮不需要跳转路径,它只负责给前端判断这个按钮该不该渲染。

3. SSM工程搭建:从JDK环境变量到MyBatis数据源必调参数

3.1 版本选型:JDK 8配Spring 5.x最稳

权限管理系统属于典型的CRUD密集型应用,追求的是稳定而不是新特性,版本选型上不需要追新。本地环境建议JDK 8,配好JAVA_HOME环境变量后,在命令行执行java -version确认版本;MySQL装5.7或8.0均可,如果是免安装版zip解压,记得在my.ini里把basedirdatadir都写成绝对路径,否则启动时会报找不到数据目录。

推荐版本组合如下,直接照搬不会遇到版本冲突:

组件推荐版本选型理由
JDK1.8SSM框架在JDK 8下的编译产物最成熟
MySQL5.7 / 8.0归档表用utf8mb4完全够用
Spring / SpringMVC5.2.x支持JDK 8,无新增依赖困扰
MyBatis3.5.x配合mybatis-spring 2.0.x
连接池Druid 1.2.x自带监控页面便于排查连接泄漏
Maven3.6+依赖管理,打包用

3.2 Maven依赖坐标:一个pom.xml拉齐SSM全家桶

SSM手工搭建最大的优势就是每个依赖都知道是干什么用的。核心依赖在pom.xml里就这些,注释的位置对应权限管理系统工作时的实际环节:

<dependencies> <!-- Spring核心与SpringMVC --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.2.15.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>5.2.15.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>5.2.15.RELEASE</version> </dependency> <!-- MyBatis与Spring整合 --> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.6</version> </dependency> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis-spring</artifactId> <version>2.0.6</version> </dependency> <!-- MySQL驱动与Druid连接池 --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.28</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.2.8</version> </dependency> <!-- Servlet API与JSON序列化 --> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>4.0.1</version> <scope>provided</scope> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.12.3</version> </dependency> <!-- AOP支持,方法级权限注解需要 --> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.9.7</version> </dependency> </dependencies>

如果环境里Maven下载依赖很慢,先在settings.xml里配置阿里云镜像,这个不展开说。连依赖坐标都无法解析时,优先检查本地Maven仓库是否有残留的.lastUpdated后缀文件,删掉对应目录重新编译即可。

3.3 数据源配置:Druid三个参数影响MySQL连接稳定性

SSM的Spring配置文件通常分成spring-context.xmlspring-mybatis.xmlspring-mvc.xml。权限管理系统不是超大规模并发,spring-mybatis.xml里最需要关注的配置是数据源和SqlSessionFactory:

<context:property-placeholder location="classpath:jdbc.properties" /> <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close"> <property name="driverClassName" value="${jdbc.driver}" /> <property name="url" value="${jdbc.url}" /> <property name="username" value="${jdbc.username}" /> <property name="password" value="${jdbc.password}" /> <property name="initialSize" value="5" /> <property name="minIdle" value="5" /> <property name="maxActive" value="20" /> <property name="maxWait" value="60000" /> <property name="validationQuery" value="SELECT 1" /> <property name="testWhileIdle" value="true" /> <property name="testOnBorrow" value="false" /> <property name="timeBetweenEvictionRunsMillis" value="60000" /> </bean> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="dataSource" /> <property name="typeAliasesPackage" value="com.demo.permission.entity" /> <property name="mapperLocations" value="classpath:mapper/*.xml" /> <property name="configuration"> <bean class="org.apache.ibatis.session.Configuration"> <property name="mapUnderscoreToCamelCase" value="true" /> </bean> </property> </bean> <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <property name="basePackage" value="com.demo.permission.dao" /> </bean>

jdbc.properties对应内容就是数据库连接四件套:jdbc.driver=com.mysql.cj.jdbc.Driverjdbc.url=jdbc:mysql://localhost:3306/permission_db?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai&useSSL=false、用户名和密码按本地环境填。serverTimezone这个参数在MySQL 8.0下必须显式指定,否则驱动会报时区错误。

Druid的initialSizeminIdlemaxActive是权限管理系统最常调的三个参数。initialSize=5表示启动时建立5个物理连接;minIdle=5控制连接池最少保留5个空闲连接;maxActive=20是并发峰值时能达到的最大连接数。validationQuery=SELECT 1是为了防止MySQL空闲超过8小时后自动断开连接,Druid在归还或获取连接前先探测一下。testWhileIdletimeBetweenEvictionRunsMillis配合,让连接池每60秒扫描一次空闲连接,踢掉失效连接。

4. 登录认证与URL级权限拦截:SSM权限系统的核心链路

4.1 令牌选型:内部后台优先Session+Cookie

权限管理系统的令牌方案在Session和JWT之间做选择。内部后台管理系统、管理端、企业OA这类场景,优先用Session+Cookie,因为服务端能随时把某个账号踢下线,改密码后旧的登录态立即失效,这个特性在权限系统里比无状态重要得多。分布式部署时把Session存储切换到Redis即可,不需要改架构。

web.xml里配置Session超时时间,单位是分钟:

<session-config> <session-timeout>120</session-timeout> <cookie-config> <http-only>true</http-only> </cookie-config> </session-config>

http-only这一项务必打开,防止前端脚本直接读取Cookie中的会话ID。Session机制下,登录成功后的标准动作就是往Session里塞用户信息和权限集合。

4.2 登录Controller:加盐哈希校验与状态检查

登录逻辑不能只做一次密码比对。完整的流程是:先按用户名查用户,再判断status是否被禁用,然后用MD5加盐哈希比对密码,最后把用户对象和权限集合写入Session。前两步失败时返回的是“账号不存在或已禁用”这类明确提示,第三步失败时统一返回“用户名或密码错误”,避免暴露账号是否存在。

@Controller public class LoginController { @Autowired private UserService userService; @Autowired private PermissionService permissionService; @RequestMapping(value = "/login", method = RequestMethod.POST) @ResponseBody public Result login(String username, String password, HttpSession session) { // 第一步:按用户名查询用户 User user = userService.findByUsername(username); if (user == null || user.getStatus() != 1) { return Result.error("账号不存在或已被禁用"); } // 第二步:MD5加盐哈希校验,盐使用用户名 String inputPwd = Md5Util.md5WithSalt(password, user.getUsername()); if (!user.getPassword().equals(inputPwd)) { return Result.error("用户名或密码错误"); } // 第三步:查询该用户拥有的所有权限编码 List<String> perms = permissionService.findPermCodesByUserId(user.getUserId()); // 第四步:会话保存用户信息和权限集合 session.setAttribute(Constants.SESSION_USER, user); session.setAttribute(Constants.SESSION_PERMS, perms); return Result.success("登录成功"); } }

salt为什么用用户名而不是随机字符串?因为登录校验密码时需要拿到同一个盐,如果盐存在数据库里就要多一次查询,直接用用户名做盐可以省掉一次DB访问,安全性上也足够。Md5Util是把MD5(password + salt)做了一轮封装的工具类,不要在这个工具类里直接暴露原生MessageDigest。权限集合SESSION_PERMS在登录时一次性查出,后面所有拦截器都只读Session,不再碰数据库。

4.3 自定义拦截器:从Session读权限并匹配请求URL

URL级权限拦截是这套权限管理系统最核心的部分。一个常见的错误是把校验写在Controller方法内部,这样每个接口都要重复写一遍权限判断。正确做法是定义一个SpringMVC拦截器,在preHandle里统一处理。

public class PermissionInterceptor implements HandlerInterceptor { private List<String> excludeUrls; // 放行白名单 public void setExcludeUrls(List<String> excludeUrls) { this.excludeUrls = excludeUrls; } @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { String uri = request.getRequestURI(); // 1. 白名单直接放行 if (excludeUrls != null && excludeUrls.contains(uri)) { return true; } // 2. 未登录用户统一拦截 HttpSession session = request.getSession(false); if (session == null || session.getAttribute(Constants.SESSION_USER) == null) { response.sendRedirect(request.getContextPath() + "/login"); return false; } // 3. 从Session获取当前用户的权限编码列表 List<String> perms = (List<String>) session.getAttribute(Constants.SESSION_PERMS); // 4. 遍历权限集合,精确或前缀匹配当前请求URI boolean hasPermission = false; for (String perm : perms) { // 权限编码存的是 /user/list 这种字符串格式 if (perm.equals(uri) || uri.startsWith(perm)) { hasPermission = true; break; } } if (!hasPermission) { response.setStatus(HttpServletResponse.SC_FORBIDDEN); response.getWriter().write("{\"code\":403,\"msg\":\"无访问权限\"}"); return false; } return true; } }

我在写权限管理系统时,习惯把权限编码的规则定为“URL即权限码”,也就是t_permission表的url字段放接口路径,拦截器直接拿request.getRequestURI()去比。这样少一层映射,维护时也直观。白名单excludeUrls在SpringMVC配置文件里声明,至少放行/login/logout、静态资源三部分。session.getSession(false)里的false表示当前没有Session时返回null而不是新建一个,避免恶意请求无谓地创建会话对象。

SpringMVC配置类里注册拦截器并指定白名单:

<mvc:interceptors> <mvc:interceptor> <mvc:mapping path="/**" /> <bean class="com.demo.permission.interceptor.PermissionInterceptor"> <property name="excludeUrls"> <list> <value>/login</value> <value>/logout</value> <value>/static/**</value> </list> </property> </bean> </mvc:interceptor> </mvc:interceptors>

注意/static/**这个写法是SpringMVC的Ant通配符规则,/*只能匹配一层路径,/**才能匹配多级目录。静态资源如果配错,CSS和JS全被拦截器吃掉,页面样式直接崩掉,这是排查频率最高的一个坑。

4.4 方法级权限校验:自定义注解加切面

URL拦截器解决的是“这一整个Controller路径能不能访问”的问题,但权限管理系统里经常遇到同一个路径下某个操作只能特定角色执行。比如/user/delete接口,管理员能调,普通操作员不能调。此时就得在方法级别加上更细的权限校验。

自定义注解在权限管理系统里是最常用、最容易扩展的方案:

@Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface RequiresPermission { String value(); // 权限编码,如 user:delete }

AOP切面配合拦截器,形成“URL权限粗筛,方法权限精筛”的双层控制:

@Aspect @Component public class PermissionAspect { @Pointcut("@annotation(com.demo.permission.annotation.RequiresPermission)") public void permissionPointcut() { } @Around("permissionPointcut()") public Object checkPermission(ProceedingJoinPoint joinPoint) throws Throwable { // 获取当前请求对应的Session ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); HttpServletRequest request = attributes.getRequest(); HttpSession session = request.getSession(); List<String> perms = (List<String>) session.getAttribute(Constants.SESSION_PERMS); // 获取注解上的权限编码 MethodSignature signature = (MethodSignature) joinPoint.getSignature(); RequiresPermission permission = signature.getMethod().getAnnotation(RequiresPermission.class); String required = permission.value(); // 权限编码匹配:持有的权限集里包含"user:*"或"user:delete"均可 boolean matched = perms.stream() .anyMatch(p -> p.equals(required) || p.endsWith(":*")); if (!matched) { throw new BusinessException(403, "无操作权限"); } return joinPoint.proceed(); } }

切面里的p.endsWith(":*")是权限通配符规则,角色拥有user:*时表示拥有用户模块的全部操作权限。在Controller方法上使用时就三行代码:

@RequiresPermission("user:delete") @RequestMapping("/user/delete") @ResponseBody public Result deleteUser(Integer userId) { userService.deleteById(userId); return Result.success(); }

这套双层校验体系跑起来后,外部请求进来先过拦截器,判断用户是否登录、访问路径是否属于其权限范围;再进AOP切面,判断具体的方法调用是否匹配细粒度权限编码。测试时可以先用管理员账号登录,然后手动修改Session里的权限集合列表,模拟不同角色访问同一个接口的返回结果。

5. 权限校验的缓存优化、五个必踩的坑与面试回答要点

5.1 登录后把权限集合放入本地缓存

权限集合每次请求都从Session取,Session本身在内存里,这步没问题。真正的性能隐患是permissionService.findPermCodesByUserId这条SQL在每次创建新Session时都会查询。用户的权限在菜单或角色变更之前是固定的,完全可以用缓存扛住。

单体部署时用Spring自带的@Cacheable最简单:

@Service public class PermissionService { @Cacheable(cacheNames = "userPerms", key = "#userId") public List<String> findPermCodesByUserId(Integer userId) { // 执行关联查询:user_role -> role_permission -> t_permission } @CacheEvict(cacheNames = "userPerms", key = "#userId") public void refreshUserPerms(Integer userId) { // 角色或权限变更后调用,清除缓存 } }

加缓存后必须配套缓存失效策略。管理员调整某个角色的权限后,要遍历该角色下的所有用户,逐个调用refreshUserPerms清缓存。如果没有清缓存,用户看到的还是旧权限,越权或权限残留的问题就是这么来的。分布式部署时把CacheManager换成Redis实现,原理相同,Key仍然是用户ID。

5.2 权限管理系统常见的五个坑和对应的面试话术

现象解法
密码用明文存储数据库泄露后账号全裸MD5加盐,后续升级为bcrypt
角色权限修改后缓存不刷用户权限变更不生效提供缓存刷新接口,业务侧调用
SQL用LIKE '%${param}%'拼接用户管理功能存在注入风险改用CONCAT('%', #{param}, '%')
拦截器白名单配错登录页面死循环重定向放行login接口和静态资源
权限关联表删了用户不删关联用户重新创建后带着旧角色删除用户时同步删除t_user_role关联

面试时如果被问到“权限管理系统的权限控制怎么做”,回答路径应该是:先讲RBAC的数据库五张表设计,再讲Session里存什么、拦截器怎么拦URL、AOP如何做方法级校验,最后补上缓存清理策略。这套链路说下来,比背八股文里的“Spring Security过滤器链”要容易让面试官听进去,因为它每一步都能画出来。权限管理系统的边界也要认真回答一句:RBAC管的是能不能访问,数据权限管的是能看哪几行数据,这两件事不混为一谈。

验证整套权限配置是否正常,最直接的方法是启动项目后用管理员账号登录拿Session,再打开浏览器无痕窗口用普通账号登录,两侧分别访问/user/list/user/delete接口,对照返回结果就能确认URL拦截和方法注解是否按预期工作。用命令行校验接口时,加-b参数带上Cookie模拟已登录状态即可完整走通权限校验链路。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询