You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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的请求被拒绝,需要同步两者的角色名称。

解决步骤

  1. 修正路径匹配:把SecurityConfig中的路径改为和Controller一致:
http.authorizeHttpRequests()
        .requestMatchers("/test-url/**").hasRole("testRole") // 修正路径和角色名称
        .anyRequest().authenticated()
        // ... 其他配置
  1. 可选:统一授权方式
    • 如果保留filter级授权,可以去掉Controller上的@PreAuthorize,避免重复检查;
    • 如果偏好方法级授权,可以简化SecurityConfig,只保留anyRequest().authenticated(),让方法级注解处理权限校验,但此时未认证请求触发方法级检查仍会返回403,若要返回401,需要额外配置:
      @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");
              }
          };
      }
      
      然后在SecurityConfig中添加:
      http.exceptionHandling().accessDeniedHandler(accessDeniedHandler());
      

测试验证

修改后重新运行noToken_unauthorized测试用例,未携带token的请求会被filter层面拦截,返回预期的401 Unauthorized。

内容的提问来源于stack exchange,提问作者LDropl

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.27 06:19:58