Spring Boot公开REST接口返回401未授权问题排查
首先梳理你的核心问题:注册、登录这类本应公开的端点无法正常访问,返回401错误,而你当前仅配置了/public/**作为公开路径——显然你的公开端点并不在这个路径范围内。结合你提供的SecurityConfig代码,我整理了几个关键问题点:
1. 公开路径范围未覆盖注册/登录端点
你的PUBLIC_URLS只定义了/public/**这一类路径:
private static final RequestMatcher PUBLIC_URLS = new OrRequestMatcher( new AntPathRequestMatcher("/public/**") );
如果你的注册接口是/api/register、登录接口是/api/login这类非/public前缀的路径,这些请求会被判定为PROTECTED_URLS(受保护路径),进而触发Spring Security的认证拦截,返回401。
修复方式:把所有需要公开的端点都加入PUBLIC_URLS集合,示例如下:
private static final RequestMatcher PUBLIC_URLS = new OrRequestMatcher( new AntPathRequestMatcher("/public/**"), new AntPathRequestMatcher("/api/register"), new AntPathRequestMatcher("/api/login") );
2. 认证拦截逻辑的触发顺序问题
你配置的默认认证入口点是返回403 Forbidden:
@Bean AuthenticationEntryPoint forbiddenEntryPoint() { return new HttpStatusEntryPoint(FORBIDDEN); }
但你实际收到的是401,这说明请求在到达这个入口点之前就被TokenAuthenticationFilter拦截了。这个过滤器会对所有PROTECTED_URLS的请求尝试认证,如果请求没有携带有效token,会抛出AuthenticationException,进而被Spring Security默认异常处理器返回401。
本质还是因为你的公开端点没被加入PUBLIC_URLS,导致请求进入了受保护的过滤器链。
3. 依赖注入的不规范写法
你在字段上用@Autowired注入TokenAuthenticationProvider,但构造函数里有一段注释掉的依赖校验代码:
//this.provider = requireNonNull(provider);
虽然目前不会直接报错,但Spring推荐使用构造函数注入,可以避免依赖注入顺序问题,也能保证依赖非空:
private final TokenAuthenticationProvider provider; @Autowired public SecurityConfig(TokenAuthenticationProvider provider) { super(); this.provider = Objects.requireNonNull(provider); System.out.println("XXXXXXXXXXXX"); System.out.println("XXXXXXXXXXXX"); }
4. 路径匹配的验证建议
你通过web.ignoring().requestMatchers(PUBLIC_URLS)让Spring Security完全忽略公开路径的拦截,这部分逻辑是正确的,但前提是你的请求路径能被AntPathRequestMatcher正确匹配。可以临时添加日志或断点,确认注册/登录请求是否真的被识别为公开路径。
总结修复步骤
- 扩展
PUBLIC_URLS,将所有需要公开的端点路径都添加进去; - 验证请求路径与
AntPathRequestMatcher的匹配规则(注意路径末尾斜杠、前缀一致性等细节); - 改用构造函数注入
TokenAuthenticationProvider,提升代码稳定性; - 测试时可通过日志确认哪些请求被Spring Security忽略。
内容的提问来源于stack exchange,提问作者janlan

