Java 中的跨域问题

06-01 816阅读

1、Java 中跨域问题的来源

跨域问题(Cross-Origin Resource Sharing, CORS)本质上是浏览器的一种安全机制,与Java本身无关,但Java后端开发者需要理解其来源以便正确解决。以下是跨域问题的详细来源分析:

1.1. 浏览器同源策略(Same-Origin Policy)

根本来源:浏览器出于安全考虑实施的同源策略

  • 同源定义:协议(http/https)+域名+端口三者完全相同
  • 限制内容:限制不同源的DOM访问,限制不同源的AJAX请求,限制不同源的Cookie/LocalStorage访问

    1.2. Java后端常见的跨域触发场景

    1.2.1 前后端分离架构

    Java 中的跨域问题

    开发时前端与后端运行在不同端口

    生产环境前端与后端可能部署在不同域名下

    1.2.2 微服务架构

    Java 中的跨域问题

    网关与服务可能在不同域

    服务间调用也可能涉及跨域

    1.2.3 第三方API集成

    调用外部服务如支付接口、地图API等

    1.3. Java中具体的跨域表现

    1.3.1 典型错误
    Access to XMLHttpRequest at 'http://api.example.com' from origin 'http://frontend.com' 
    has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present 
    on the requested resource.
    
    1.3.2 触发条件

    Java 中的跨域问题

    1.4. Java特有的跨域问题来源

    1.4.1 Spring Security默认配置

    Spring Security默认启用CSRF保护

    会与CORS机制产生冲突

    1.4.2 Servlet容器行为

    Tomcat/Jetty等容器默认不带CORS头

    过滤器链顺序可能影响CORS处理

    1.4.3 传统Java Web应用

    JSP时代页面和后端同源,现代前后端分离导致问题显现

    1.5. 为什么需要Java端解决

    浏览器行为不可控:同源策略是浏览器强制实施的

    安全责任在后端:哪些源可以访问应由后端决定

    灵活控制需求:不同接口可能需要不同的跨域策略

    1.6. 特殊注意事项

    Cookie跨域:需要设置Access-Control-Allow-Credentials: true

    自定义头跨域:需在Access-Control-Allow-Headers中声明

    缓存问题:合理设置Access-Control-Max-Age提高性能

    2、Java 中解决跨域问题的方法

    跨域问题是由于浏览器的同源策略(Same-Origin Policy)导致的,当你的前端应用(如运行在 http://localhost:8080)尝试访问不同源(如 http://api.example.com)的后端API时,浏览器会阻止这种请求。以下是Java中常见的跨域解决方案:

    2.1. Spring Boot 解决方案

    2.1.1 使用 @CrossOrigin 注解
    @RestController
    @RequestMapping("/api")
    public class MyController {
        
        // 允许单个方法跨域
        @CrossOrigin(origins = "http://localhost:3000")
        @GetMapping("/hello")
        public String hello() {
            return "Hello, CORS!";
        }
        
        // 允许整个控制器跨域
        @CrossOrigin(origins = "http://localhost:3000")
        @GetMapping("/another")
        public String another() {
            return "Another endpoint";
        }
    }
    
    2.1.2 全局配置跨域
    @Configuration
    public class CorsConfig implements WebMvcConfigurer {
        
        @Override
        public void addCorsMappings(CorsRegistry registry) {
            registry.addMapping("/**")  // 所有路径
                    .allowedOrigins("http://localhost:3000", "https://example.com")  // 允许的源
                    .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")  // 允许的方法
                    .allowedHeaders("*")  // 允许的请求头
                    .allowCredentials(true)  // 允许携带凭证(cookie等)
                    .maxAge(3600);  // 预检请求的缓存时间(秒)
        }
    }
    

    2.2. 传统 Servlet 解决方案

    2.2.1 使用 Filter
    /**
     * CORS跨域过滤器配置
     * 用于处理浏览器跨域请求的支持
     * 过滤器会拦截所有请求(/*)并添加CORS响应头
     */
    @WebFilter("/*")  // 拦截所有请求
    public class CorsFilter implements Filter {
        /**
         * 过滤器核心方法,处理请求和响应
         * @param req ServletRequest对象
         * @param res ServletResponse对象
         * @param chain FilterChain对象,用于继续过滤器链
         */
        @Override
        public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) 
                throws IOException, ServletException {
            // 类型转换为HTTP相关的请求/响应对象
            HttpServletResponse response = (HttpServletResponse) res;
            HttpServletRequest request = (HttpServletRequest) req;
            // 设置允许所有域访问(生产环境应替换为具体域名)
            response.setHeader("Access-Control-Allow-Origin", "*");
            
            // 设置允许的HTTP方法
            response.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
            
            // 设置预检请求的缓存时间(1小时)
            response.setHeader("Access-Control-Max-Age", "3600");
            
            // 设置允许的请求头(包括自定义头)
            response.setHeader("Access-Control-Allow-Headers", "authorization, content-type, xsrf-token");
            
            // 设置允许前端访问的响应头(暴露自定义头)
            response.addHeader("Access-Control-Expose-Headers", "xsrf-token");
            // 处理OPTIONS预检请求
            if ("OPTIONS".equals(request.getMethod())) {
                // 直接返回200状态码,不继续过滤器链
                response.setStatus(HttpServletResponse.SC_OK);
            } else {
                // 非OPTIONS请求,继续过滤器链
                chain.doFilter(req, res);
            }
        }
        /**
         * 过滤器初始化方法(可留空)
         * @param filterConfig 过滤器配置对象
         */
        @Override 
        public void init(FilterConfig filterConfig) {
            // 初始化逻辑(如有需要)
        }
        /**
         * 过滤器销毁方法(可留空)
         */
        @Override 
        public void destroy() {
            // 清理资源逻辑(如有需要)
        }
    }
    

    2.3. Spring Security 解决方案

    如果你的应用使用了Spring Security,需要在安全配置中添加CORS支持:

    /**
     * Spring Security 安全配置类
     * 用于配置应用的安全策略和CORS跨域设置
     */
    @Configuration  // 标记为Spring配置类
    @EnableWebSecurity  // 启用Spring Security的Web安全支持
    public class SecurityConfig extends WebSecurityConfigurerAdapter {
        /**
         * 配置HTTP安全策略
         * @param http HttpSecurity对象,用于配置安全策略
         */
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http
                // 启用CORS支持(使用下面定义的corsConfigurationSource bean)
                .cors().and()  
                
                // 禁用CSRF防护(跨站请求伪造),因为API通常使用token验证而非session
                // 注意:如果前端与后端同域且使用session,应该保持启用
                .csrf().disable()  
                
                // 开始配置请求授权规则
                .authorizeRequests()
                    // 允许/api/public/开头的URL无需认证
                    .antMatchers("/api/public/**").permitAll()  
                    
                    // 其他所有请求都需要认证
                    .anyRequest().authenticated();
        }
        /**
         * 配置CORS跨域设置
         * @return CorsConfigurationSource 跨域配置源
         */
        @Bean
        public CorsConfigurationSource corsConfigurationSource() {
            // 创建CORS配置对象
            CorsConfiguration configuration = new CorsConfiguration();
            
            // 设置允许的源(前端地址),可以添加多个
            configuration.setAllowedOrigins(Arrays.asList("http://localhost:3000"));
            
            // 设置允许的HTTP方法
            configuration.setAllowedMethods(Arrays.asList(
                "GET",      // 获取资源
                "POST",     // 创建资源
                "PUT",      // 更新资源
                "DELETE",   // 删除资源
                "OPTIONS"   // 预检请求
            ));
            
            // 设置允许的请求头(*表示所有)
            configuration.setAllowedHeaders(Arrays.asList("*"));
            
            // 允许发送凭据(cookie、认证信息等)
            // 注意:当设置为true时,allowedOrigins不能为*
            configuration.setAllowCredentials(true);
            
            // 创建基于URL的CORS配置源
            UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
            
            // 对所有URL路径应用上述CORS配置
            source.registerCorsConfiguration("/**", configuration);
            
            return source;
        }
    }
    

    2.4. 注意事项

    生产环境:不要使用 * 作为允许的源,应该明确指定允许的域名

    凭证:如果前端需要发送cookie等凭证信息,需要设置 allowCredentials(true),并且不能使用 * 作为允许的源

    预检请求:对于复杂请求(如带自定义头的请求),浏览器会先发送OPTIONS预检请求

    缓存:合理设置 maxAge 可以减少预检请求的次数

    2.5. 测试跨域是否成功

    在浏览器开发者工具中检查响应头是否包含:

    Access-Control-Allow-Origin: http://your-frontend-domain
    Access-Control-Allow-Methods: GET, POST, PUT, DELETE
    Access-Control-Allow-Headers: content-type
    

    Spring Boot应用推荐使用全局配置或Spring Security配置的方式。

免责声明:我们致力于保护作者版权,注重分享,被刊用文章因无法核实真实出处,未能及时与作者取得联系,或有版权异议的,请联系管理员,我们会立即处理! 部分文章是来自自研大数据AI进行生成,内容摘自(百度百科,百度知道,头条百科,中国民法典,刑法,牛津词典,新华词典,汉语词典,国家院校,科普平台)等数据,内容仅供学习参考,不准确地方联系删除处理! 图片声明:本站部分配图来自人工智能系统AI生成,觅知网授权图片,PxHere摄影无版权图库和百度,360,搜狗等多加搜索引擎自动关键词搜索配图,如有侵权的图片,请第一时间联系我们。

相关阅读

目录[+]

取消
微信二维码
微信二维码
支付宝二维码