Java框架快速入门: Spring Security+OAuth2之跨域处理

Java框架快速入门: Spring Security+OAuth2之跨域处理 纲要跨域问题背景浏览器同源策略与CORS错误前后端分离开发中的典型跨域场景跨域解决方案概览前端开发服务器代理后端CORS配置Spring MVC 中的全局 CORS 配置WebMvcConfigurer与addCorsMappings允许来源、方法、头部设置Spring Security 中的 CORS 配置自定义CorsConfigurationSourceBean按环境区分配允许的来源开发 / 生产暴露自定义响应头X-Authenticity等在安全过滤链中启用 CORS完整可运行配置示例验证与踩坑响应头暴露的重要性跨域请求失败时的排查思路跨域问题背景现代前后端分离项目中前端应用通常运行在localhost:4001而后端 API 可能部署在localhost:8080。当浏览器从不同源协议、域名、端口任一不同发起 HTTP 请求时就会触发浏览器的同源策略出现类似以下的 CORS 错误Access to XMLHttpRequest at ‘http://localhost:8080/authorize/token’ from origin ‘http://localhost:4001’ has been blocked by CORS policy: No ‘Access-Control-Allow-Origin’ header is present on the requested resource.这意味着后端服务需要显式声明允许哪些源访问否则浏览器会拦截响应。跨域解决方案概览解决跨域访问通常有两种思路前端代理利用前端开发服务器如 webpack-dev-server将 API 请求代理到后端从而变为同源访问。后端 CORS 配置在服务端添加Access-Control-Allow-Origin等响应头让浏览器允许跨域请求。在 Spring 生态中后端配置 CORS 更为通用并且不依赖前端工具链下面重点讲解后端的配置方式。Spring MVC 中的全局 CORS 配置如果项目未引入 Spring Security可以直接在WebMvcConfigurer中配置全局 CORS 规则importorg.springframework.context.annotation.Configuration;importorg.springframework.web.servlet.config.annotation.CorsRegistry;importorg.springframework.web.servlet.config.annotation.WebMvcConfigurer;ConfigurationpublicclassGlobalCorsConfigimplementsWebMvcConfigurer{OverridepublicvoidaddCorsMappings(CorsRegistryregistry){registry.addMapping(/**)// 所有接口.allowedOrigins(http://localhost:4001)// 允许的前端源.allowedMethods(GET,POST,PUT,DELETE,OPTIONS).allowedHeaders(*).allowCredentials(true);}}这种方式适用于纯 Spring MVC 项目但它不会影响 Spring Security 的过滤器链。Spring Security 中的 CORS 配置在整合了 Spring Security 的 OAuth2 授权服务中跨域请求会先经过 Security 过滤器链。如果仅在 MVC 层配置 CORSSecurity 层的请求仍会被拦截。正确做法是在 Spring Security 中显式启用 CORS 并提供一个CorsConfigurationSourceBean。自定义 CorsConfigurationSource创建一个配置类通过Value读取外部配置动态设置允许的源并将CorsConfigurationSource注册到 Spring 容器中。这里特意暴露了自定义响应头X-Authenticity用于二次认证场景。packagecom.example.config;importorg.springframework.beans.factory.annotation.Value;importorg.springframework.context.annotation.Bean;importorg.springframework.context.annotation.Configuration;importorg.springframework.web.cors.CorsConfiguration;importorg.springframework.web.cors.CorsConfigurationSource;importorg.springframework.web.cors.UrlBasedCorsConfigurationSource;importjava.util.Arrays;ConfigurationpublicclassCorsConfig{Value(${cors.allowed-origins:http://localhost:4001})privateStringallowedOrigins;BeanpublicCorsConfigurationSourcecorsConfigurationSource(){CorsConfigurationconfigurationnewCorsConfiguration();configuration.setAllowedOrigins(Arrays.asList(allowedOrigins.split(,)));configuration.setAllowedMethods(Arrays.asList(GET,POST,PUT,DELETE,OPTIONS));configuration.setAllowedHeaders(Arrays.asList(*));// 重要暴露自定义响应头例如二次认证时返回的 X-Authenticityconfiguration.setExposedHeaders(Arrays.asList(X-Authenticity,Authorization));configuration.setAllowCredentials(true);UrlBasedCorsConfigurationSourcesourcenewUrlBasedCorsConfigurationSource();source.registerCorsConfiguration(/**,configuration);returnsource;}}在安全配置中启用 CORS在 Security 配置类中注入CorsConfigurationSource并通过cors()方法启用packagecom.example.config;importorg.springframework.context.annotation.Bean;importorg.springframework.context.annotation.Configuration;importorg.springframework.security.config.annotation.web.builders.HttpSecurity;importorg.springframework.security.web.SecurityFilterChain;importorg.springframework.web.cors.CorsConfigurationSource;ConfigurationpublicclassSecurityConfig{privatefinalCorsConfigurationSourcecorsConfigurationSource;publicSecurityConfig(CorsConfigurationSourcecorsConfigurationSource){this.corsConfigurationSourcecorsConfigurationSource;}BeanpublicSecurityFilterChainfilterChain(HttpSecurityhttp)throwsException{http.cors(cors-cors.configurationSource(corsConfigurationSource)).authorizeHttpRequests(auth-auth.anyRequest().authenticated()).oauth2ResourceServer(oauth2-oauth2.jwt());// 示例 OAuth2 配置returnhttp.build();}}为什么需要暴露自定义响应头在 OAuth2 二次验证流程中后端可能会在401响应里附加X-Authenticity头提示客户端需要进行下一步认证。对于跨域请求浏览器默认只暴露Cache-Control、Content-Language、Content-Type、Expires、Last-Modified、Pragma等少量简单响应头。如果不通过setExposedHeaders()显式声明前端 JavaScript 代码将无法读取X-Authenticity导致认证流程中断可能看到500错误或无响应头的情况。跨域请求流程示意以下时序图展示了前端发出跨域请求后端配置 CORS 后顺利返回自定义响应头的过程资源服务器Spring Security 过滤器浏览器 (localhost:4001)资源服务器Spring Security 过滤器浏览器 (localhost:4001)前端可读取 X-Authenticity 并继续流程OPTIONS /api/authorize (预检)200 OK (Access-Control-Allow-Origin: localhost:4001)POST /api/authorize (真实请求)转发认证401 X-Authenticity: required401 X-Authenticity (暴露)完整项目配置示例以下为 Spring Boot 项目的核心配置文件及结构可直接运行。项目结构src/main/java/com/example/config/ ├── CorsConfig.java └── SecurityConfig.java src/main/resources/ ├── application.ymlapplication.ymlcors:allowed-origins:http://localhost:4001spring:security:oauth2:resourceserver:jwt:issuer-uri:https://your-issuerCorsConfig.javapackagecom.example.config;importorg.springframework.beans.factory.annotation.Value;importorg.springframework.context.annotation.Bean;importorg.springframework.context.annotation.Configuration;importorg.springframework.web.cors.CorsConfiguration;importorg.springframework.web.cors.CorsConfigurationSource;importorg.springframework.web.cors.UrlBasedCorsConfigurationSource;importjava.util.Arrays;ConfigurationpublicclassCorsConfig{Value(${cors.allowed-origins:http://localhost:4001})privateStringallowedOrigins;BeanpublicCorsConfigurationSourcecorsConfigurationSource(){CorsConfigurationconfigurationnewCorsConfiguration();configuration.setAllowedOrigins(Arrays.asList(allowedOrigins.split(,)));configuration.setAllowedMethods(Arrays.asList(GET,POST,PUT,DELETE,OPTIONS));configuration.setAllowedHeaders(Arrays.asList(*));configuration.setExposedHeaders(Arrays.asList(X-Authenticity,Authorization));configuration.setAllowCredentials(true);UrlBasedCorsConfigurationSourcesourcenewUrlBasedCorsConfigurationSource();source.registerCorsConfiguration(/**,configuration);returnsource;}}SecurityConfig.javapackagecom.example.config;importorg.springframework.context.annotation.Bean;importorg.springframework.context.annotation.Configuration;importorg.springframework.security.config.annotation.web.builders.HttpSecurity;importorg.springframework.security.web.SecurityFilterChain;importorg.springframework.web.cors.CorsConfigurationSource;ConfigurationpublicclassSecurityConfig{privatefinalCorsConfigurationSourcecorsConfigurationSource;publicSecurityConfig(CorsConfigurationSourcecorsConfigurationSource){this.corsConfigurationSourcecorsConfigurationSource;}BeanpublicSecurityFilterChainfilterChain(HttpSecurityhttp)throwsException{http.cors(cors-cors.configurationSource(corsConfigurationSource)).authorizeHttpRequests(auth-auth.anyRequest().authenticated()).oauth2ResourceServer(oauth2-oauth2.jwt());returnhttp.build();}}验证与踩坑预检请求必须通过Spring Security 配置 CORS 后OPTIONS请求会被 CORS 过滤器处理并返回 200无需额外放行。响应头暴露缺失若未调用setExposedHeaders即使后端设置了X-Authenticity浏览器也会阻止 JavaScript 读取该头导致认证失败。可根据业务需要添加需要暴露的头名称。多环境切换通过Value或Profile灵活指定allowed-origins开发环境使用localhost:4001生产环境使用真实域名。调试技巧打开浏览器开发者工具 Network 面板检查响应头中是否包含Access-Control-Allow-Origin和Access-Control-Expose-Headers确认配置生效。总结本文详细梳理了在 Spring Security OAuth2 项目中处理跨域访问的常见方案。重点在于 Spring Security 侧的CorsConfigurationSource配置以及暴露自定义响应头的重要性。通过合理配置前端可以无缝发起跨域认证请求并完整读取后端返回的认证提示头保障二次认证流程的顺利执行。