Spring Boot OAuth2端点JUnit测试异常:返回Forbidden而非Unauthorized
问题原因及解决办法
核心问题1:安全配置的路径匹配错误
你的SecurityConfig中配置的路径是/testurl/**,但Controller的请求路径是/test-url,两者不匹配。这导致该接口没有走filter层面的hasRole授权规则,而是落到了anyRequest().authenticated()上。同时因为Controller方法上有@PreAuthorize("hasRole('testRole')"),当未携带token的请求到达时,方法级权限检查会先于完整的认证流程触发:此时Authentication对象为空,hasRole判断直接失败,抛出AccessDeniedException,对应HTTP 403 Forbidden,而非预期的401 Unauthorized。
核心问题2:角色名称不一致(额外隐患)
SecurityConfig中使用hasRole("test-role"),但Controller的@PreAuthorize用的是hasRole("testRole"),两者角色名称不统一,即使路径匹配后,也可能导致合法token的请求被拒绝,需要同步两者的角色名称。
解决步骤
- 修正路径匹配:把
SecurityConfig中的路径改为和Controller一致:
http.authorizeHttpRequests() .requestMatchers("/test-url/**").hasRole("testRole") // 修正路径和角色名称 .anyRequest().authenticated() // ... 其他配置
- 可选:统一授权方式
- 如果保留filter级授权,可以去掉Controller上的
@PreAuthorize,避免重复检查; - 如果偏好方法级授权,可以简化SecurityConfig,只保留
anyRequest().authenticated(),让方法级注解处理权限校验,但此时未认证请求触发方法级检查仍会返回403,若要返回401,需要额外配置:
然后在SecurityConfig中添加:@Bean public AccessDeniedHandler accessDeniedHandler() { return (request, response, accessDeniedException) -> { if (SecurityContextHolder.getContext().getAuthentication() == null) { response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized"); } else { response.sendError(HttpServletResponse.SC_FORBIDDEN, "Forbidden"); } }; }http.exceptionHandling().accessDeniedHandler(accessDeniedHandler());
- 如果保留filter级授权,可以去掉Controller上的
测试验证
修改后重新运行noToken_unauthorized测试用例,未携带token的请求会被filter层面拦截,返回预期的401 Unauthorized。
内容的提问来源于stack exchange,提问作者LDropl
相关产品推荐
相关产品推荐

