原创

彻底搞懂什么是跨域以及如何解决跨域问题?

温馨提示:
本文最后更新于 2024年11月04日,已超过 79 天没有更新。若文章内的图片失效(无法正常加载),请留言反馈或直接联系我

什么是跨域?

跨域是指一个域名网页尝试访问另一个域名下的数据或资源时,由于浏览器的同源策略限制而产生的行为。同源策略是一种安全机制,它要求网页只能访问本域名下的服务器数据或资源。当协议、域名或端口中有任意一个不同时,就会产生跨域问题。例如:当本地8080端口请求8081端口数据时,会产生跨域异常,如下:
跨域错误

为什么会产生跨域问题?

跨域问题的产生主要是由于以下几个原因:

  • 安全性:同源策略可以防止网站数据被其它恶意网站读取或修改
  • 隐私保护:本网站数据内容应该受到保护,不应该被其它网站访问
  • 内容隔离:浏览器为了保护用户的安全,将不同源的内容进行隔离

如何解决跨域问题?

在Java后端开发中,解决跨域问题通常有以下几种方法:

1. CORS (Cross-Origin Resource Sharing)

当出现跨域请求时,浏览器通常会发起两次请求一个预检请求和一个实际请求。为了处理跨域请求,CORS(跨源资源共享)机制允许服务器标示除了它自己以外的其他源,使得浏览器允许这些源访问加载自己的资源。CORS通过预检(HTTP OPTIONS)请求来检查服务器是否会允许要发送的真实请求。在预检中,浏览器发送的头中带有HTTP方法和真实请求中会用到的头信息。如果服务器响应表示允许该跨域请求,则浏览器会继续发送实际的请求。

CORS是一种W3C标准,它允许服务器放宽同源策略的限制。在跨域请求中,浏览器会自动添加一些特定的头部信息,如OriginReferer,并且不允许修改某些头部信息,如Content-Type,除非服务器允许。通过在响应头中添加特定的CORS头信息,可以实现跨域资源共享。

@CrossOrigin注解

通过@CrossOrigin注解实现方法或类跨域支持,但这种处理方法只适合少量接口支持跨域请求,当大量接口支持跨域请求时,每个接口或类上都需要添加,比较麻烦,而且如果忘记了会导致前端请求异常

@RestController
@RequestMapping("/cros")
public class TestController {
    @CrossOrigin(origins = "*")
    @GetMapping("/test")
    public String list() {
        return "Success";
    }
}

跨域

实现WebMvcConfigurer接口

实现WebMvcConfigurer接口并重写全局跨域规则,该规则全局生效,当大量接口支持跨域时,可在此一次性添加跨域规则

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/cros/**")
                .allowCredentials(true)
                .allowedOrigins("http://localhost:8080")
                .allowedMethods("GET")
                .allowedHeaders("*")
                .exposedHeaders("**");
    }
}

自定义CorsFilter过滤器

创建跨域配置类, 并在类中自定义跨域过滤器,设置自定义跨域规则,该规则同样全局生效。

@Configuration
public class CrosConfig {
    @Bean
    public CorsFilter corsFilter() {
        CorsConfiguration config = new CorsConfiguration();
        config.addAllowedOrigin("http://localhost:8080");
        config.setAllowCredentials(true);
        config.addAllowedMethod("GET");
        config.addAllowedHeader("*");
        config.addExposedHeader("**");
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/cros/**", config);
        return new CorsFilter(corsConfigurationSource);
    }
}

实现Filter接口

添加配置类实现Filter接口并重新doFilter规则,实现自定义过滤规则。doFilter会在每次请求处理时被调用,对当前请求数据进行特定处理

@Configuration
public class CrosFilter implements Filter {
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        HttpServletResponse httpServletResponse = (HttpServletResponse) response;
        httpServletResponse.setHeader("Access-Control-Allow-Origin", "http://localhost:8080");
        httpServletResponse.setHeader("Access-Control-Allow-Methods", "GET");
        chain.doFilter(request, response);
    }
}

2. 使用Nginx配置

前端ajax请求本地路径,并在请求路径中添加固定前缀,如:/api,然后通过修改Nginx的配置,此时所有http://localhost:8080/api/* 请求都会被nginx转发到指定路径,从而解决跨域问题。

在Nginx配置文件中,可以添加以下配置来实现反向代理:

location /api {
    proxy_pass http://localhost:8081;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}

3. 使用WebSocket

WebSocket是一种通信协议,它允许在单个TCP连接上进行全双工通信,从而绕过了同源策略的限制。

在Java后端,可以使用Java API for WebSocket (JSR 356)来实现WebSocket服务:

@Component
@ServerEndpoint("/websocket")
public class WebsocketServer {
    @OnOpen
    public void onOpen(Session session) {
        // ...
    }

    @OnMessage
    public void onMessage(String message, Session session) {
        // ...
    }

    @OnClose
    public void onClose(Session session) {
        // ...
    }
}

4. 使用document.domain

document.domain是一个用于获取或设置当前文档域名的属性。该方法适用于同一父域下不同子域间的跨域,通过设置document.domain为相同的值来共享数据。例如,在JavaScript中,设置:

document.domain = "example.com";

总结

跨域是Web开发中常见的问题,有多种方法可以解决该问题。在实际开发中,据具体的应用场景和技术要求选择合适的解决方案。同时,需要注意安全性、兼容性以及性能等因素,确保解决方案的可靠性和稳定性。

正文到此结束
本文目录