Postman 调通而浏览器 Ajax 失败,不能马上认定业务接口认证错误。浏览器可能先发 OPTIONS 预检,而 Postman 不按浏览器的同源策略拦截响应。旧版 OAuth2 排障中,我遇到的是 CORS 与 Spring Security 过滤器链交织的问题。

OPTIONS 匹配不等于真实请求已经放行

2019 年的配置把过滤器链请求匹配写成类似下面的片段:

http.requestMatchers()
    .antMatchers(org.springframework.http.HttpMethod.OPTIONS,
        "/login", "/oauth/authorize", "/oauth/token");

它选择“哪些请求使用这条链”,不是设置链内所有 URL 的授权规则。这里只匹配 OPTIONS;相同路径上的 GET、POST 还可能进入其他链。当时去掉方法限定后流程恢复,但不能推广成所有项目都应照改:多条链的顺序、覆盖范围和链内授权必须一起核对。

把预检交给合适的 CORS 处理

预检通常不携带登录会话所依赖的 Cookie,若先要求已认证,浏览器就不能继续真实请求。Spring Security 5.2 提供 CorsConfigurationSource 集成入口。Spring Security 5.2 CORS

下面是同代 API 的配置结构,不是整套安全配置:

@Bean
public org.springframework.web.cors.CorsConfigurationSource
        corsConfigurationSource() {
    org.springframework.web.cors.CorsConfiguration cors =
            new org.springframework.web.cors.CorsConfiguration();
    cors.setAllowedOrigins(java.util.Collections.singletonList(
            "https://frontend.example.invalid"));
    cors.setAllowedMethods(java.util.Arrays.asList("GET", "POST", "OPTIONS"));
    cors.setAllowedHeaders(java.util.Arrays.asList(
            "Authorization", "Content-Type"));
    cors.setAllowCredentials(true);
    org.springframework.web.cors.UrlBasedCorsConfigurationSource source =
            new org.springframework.web.cors.UrlBasedCorsConfigurationSource();
    source.registerCorsConfiguration("/api/**", cors);
    return source;
}

在已有 configure(HttpSecurity http) 中启用 http.cors(),保留所需认证、授权和 CSRF 规则。示例只覆盖 /api/**,登录及 OAuth 路径需要按真实目标另行明确配置。示意域名必须换成受信任前端;credentials 只在确实需要凭据时打开,不与任意来源通配搭配。

沿两次请求分别检查

先看 OPTIONS 的 Origin、目标方法和目标请求头,再确认预检响应;然后确认真正的 GET/POST 是否发送、命中哪条链及哪条授权规则。预检成功不是业务登录成功,业务返回安全错误时也需保留正确的 CORS 响应。

2020 年另一条记录使用 http.cors() 解决类似入口问题。它提示应采用框架集成,而不意味着手写 CorsFilter 固定标注 @Order(-1) 能适配所有部署。本文解释旧 API 的机制,不把历史 OAuth 配置当作新项目模板。