Spring MVC+REST权限异常:EMPLOYEE角色可非法访问POST接口
看起来你遇到的核心问题是:表单认证的权限控制正常,但REST接口的URL级授权规则完全不生效,EMPLOYEE角色用户能访问需要MANAGER/ADMIN权限的POST /customer接口。我来帮你一步步排查和解决:
一、先排查最可能的原因:路径匹配规则的细节问题
你的SecurityConfig里的规则顺序是正确的(先匹配具体路径,再匹配通配符),但很可能是请求路径的末尾斜杠导致规则匹配失败:
- Spring Security的
antMatchers(HttpMethod.POST, "/customer")是精确匹配不带末尾斜杠的请求; - 如果你测试时发送的是
POST /customer/(带末尾斜杠),这个请求会跳过POST规则,直接匹配后面的/customer/**规则(该规则允许EMPLOYEE访问)。
修复方案:
修改POST请求的权限规则,同时匹配带/不带末尾斜杠的路径:
.antMatchers(HttpMethod.POST, "/customer", "/customer/").hasAnyRole("MANAGER", "ADMIN")
或者更简洁地覆盖所有以/customer开头的POST请求(如果不需要限制子路径的POST权限):
.antMatchers(HttpMethod.POST, "/customer/**").hasAnyRole("MANAGER", "ADMIN")
二、启用方法级安全作为兜底(避免URL规则的坑)
你尝试过用@Secured但无效,是因为没有启用方法级安全支持。在你的SecurityConfig类上添加注解:
@Configuration @EnableWebSecurity @EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true) // 新增这行 public class SecurityConfig extends WebSecurityConfigurerAdapter { // ... 原有代码不变 }
然后在REST接口的方法上添加更可靠的方法级权限校验:
@PostMapping @PreAuthorize("hasAnyRole('MANAGER', 'ADMIN')") // 推荐使用PreAuthorize,更灵活 public Customer addCustomer(@RequestBody Customer customer) { customer.setId(0); customerService.saveCustomer(customer); return customer; }
(如果坚持用@Secured,记得要写全角色前缀:@Secured({"ROLE_MANAGER", "ROLE_ADMIN"}))
三、验证当前用户的实际权限(排除UserService的问题)
为了确认不是用户角色赋值错误,可以在REST接口中打印当前用户的权限信息,排查EMPLOYEE用户是否被错误赋予了更高权限:
@RestController @RequestMapping("/customer") public class CustomerRestController { @Autowired private CustomerService customerService; @Autowired private Authentication authentication; // 注入Authentication对象 @PostMapping @PreAuthorize("hasAnyRole('MANAGER', 'ADMIN')") public Customer addCustomer(@RequestBody Customer customer) { // 打印当前用户的权限 System.out.println("当前用户权限:" + authentication.getAuthorities()); customer.setId(0); customerService.saveCustomer(customer); return customer; } // ... 其他方法不变 }
如果输出里包含ROLE_MANAGER或ROLE_ADMIN,那说明你的UserService在加载用户权限时出现了错误,需要排查数据库或UserDetails的实现。
四、确认Spring Security过滤器链覆盖所有请求
确保你的DispatcherServlet映射是/(默认配置),这样所有请求(包括REST和Web表单)都会经过Spring Security的过滤器链。如果DispatcherServlet被配置为只映射/web/*之类的路径,REST请求可能会绕过Security拦截。
内容的提问来源于stack exchange,提问作者bilarsen

