跨域访问(CORS与代理)

在Web开发中,我们经常会遇到跨域的问题。跨域是指浏览器不能访问不同源(域名、协议或端口)的资源,这是浏览器为了安全而实施的同源策略。但是有时候我们确实需要访问其他源的资源,比如调用第三方的API或服务。这时候,我们就需要使用一些方法来解决跨域的问题。

CORS(Cross-Origin Resource Sharing,跨源资源共享)和代理(Proxy)是两种常用的解决跨域问题的方法。本文将介绍它们的原理和实现,并给出一些基于Spring Boot框架的示例代码。

CORS

CORS是一种基于HTTP头的机制,它允许服务器声明哪些源可以访问它的资源。浏览器在发送跨域请求时,会根据服务器返回的HTTP头来判断是否允许该请求。如果服务器允许,浏览器就可以正常访问该资源;如果服务器拒绝,浏览器就会抛出一个错误,并拦截该请求。

CORS涉及到以下几种HTTP头:

  • Origin:请求头,表示请求来源的源(域名、协议和端口)。
  • Access-Control-Allow-Origin:响应头,表示允许访问的源,可以是具体的源,也可以是*表示任意源。
  • Access-Control-Request-Method:预检请求头,表示实际请求将使用的方法。
  • Access-Control-Allow-Methods:预检响应头,表示允许访问的方法。
  • Access-Control-Request-Headers:预检请求头,表示实际请求将携带的自定义头。
  • Access-Control-Allow-Headers:预检响应头,表示允许携带的自定义头。
  • Access-Control-Allow-Credentials:响应头,表示是否允许携带身份凭证(如Cookie或HTTP认证)。
  • Access-Control-Max-Age:预检响应头,表示预检请求的结果可以缓存多久。

根据请求是否需要发送预检请求(OPTIONS方法),CORS分为简单请求和非简单请求两种。

简单请求满足以下条件:

  • 使用GET、HEAD或POST方法之一。
  • 除了浏览器自动设置的头外,只能携带以下安全的头:Accept、Accept-Language、Content-Language、Content-Type(只能是text/plain、multipart/form-data或application/x-www-form-urlencoded之一)。
  • 不使用ReadableStream对象作为请求体。

对于简单请求,浏览器直接发送实际请求,并在响应中检查Access-Control-Allow-Origin头是否匹配当前源。如果匹配,浏览器就可以获取该资源;如果不匹配,浏览器就会抛出一个错误,并拦截该请求。

非简单请求不满足以上条件。对于非简单请求,浏览器先发送一个预检请求(OPTIONS方法),询问服务器是否允许该跨域请求。服务器需要返回所有相关的CORS头来回应预检请求。如果服务器同意该跨域请求,浏览器才会发送实际请求;如果服务器拒绝该跨域请求,浏览器就会抛出一个错误,并拦截该请求。

Spring Boot中实现CORS

在Spring Boot中,有多种方式来实现CORS:

  • 在Controller类或方法上使用@CrossOrigin注解,来指定允许访问的源、方法、头等。
  • 在WebMvcConfigurer接口中重写addCorsMappings方法,来全局配置CORS。
  • 在Filter中自定义CORS过滤器,来手动设置响应头。

下面给出一些示例代码:

使用@CrossOrigin注解

假设我们有一个UserController类,提供了一个获取所有用户信息的接口。我们想要允许任意源访问该接口,并且允许携带身份凭证。我们可以在该类或方法上使用@CrossOrigin注解,并设置相应的属性:

package com.example.demo.controller;

import com.example.demo.entity.User;
import com.example.demo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

@RestController
@CrossOrigin(origins = "*", allowCredentials = "true") // 允许任意源,并且允许携带身份凭证
public class UserController {

    @Autowired
    private UserService userService;

    @GetMapping("/api/users")
    public List<User> findAll() {
        return userService.findAll();
    }
}
使用WebMvcConfigurer接口

假设我们想要全局配置CORS,允许所有的接口都可以被任意源访问,并且允许携带身份凭证。我们可以创建一个配置类,实现WebMvcConfigurer接口,并重写addCorsMappings方法:

package com.example.demo.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class CorsConfig implements WebMvcConfigurer {

    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**") // 匹配所有的接口
                .allowedOrigins("*") // 允许任意源
                .allowedMethods("*") // 允许任意方法
                .allowedHeaders("*") // 允许任意头
                .allowCredentials(true) // 允许携带身份凭证
                .maxAge(3600); // 预检请求的缓存时间
    }
}
使用Filter过滤器

假设我们想要自定义CORS过滤器,来手动设置响应头。我们可以创建一个过滤器类,实现Filter接口,并重写doFilter方法:

package com.example.demo.filter;

import org.springframework.stereotype.Component;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@Component
public class CorsFilter implements Filter {

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest httpServletRequest = (HttpServletRequest) request;
        HttpServletResponse httpServletResponse = (HttpServletResponse) response;
        // 获取请求头中的Origin字段
        String origin = httpServletRequest.getHeader("Origin");
        if (origin != null) {
            // 设置响应头中的Access-Control-Allow-Origin字段为请求头中的Origin字段
            httpServletResponse.setHeader("Access-Control-Allow-Origin", origin);
            // 设置响应头中的Access-Control-Allow-Credentials字段为true,表示允许携带身份凭证
            httpServletResponse.setHeader("Access-Control-Allow-Credentials", "true");
            // 设置响应头中的Access-Control-Allow-Methods字段为*,表示允许任意方法
            httpServletResponse.setHeader("Access-Control-Allow-Methods", "*");
            // 设置响应头中的Access-Control-Allow-Headers字段为*,表示允许任意头
            httpServletResponse.setHeader("Access-Control-Allow-Headers", "*");
            // 设置响应头中的Access-Control-Max-Age字段为3600,表示预检请求的缓存时间为3600秒
            httpServletResponse.setHeader("Access-Control-Max-Age", "3600");
        }
        chain.doFilter(request, response);
    }
}

代理

代理是一种中间层,它可以在客户端和服务器之间转发请求和响应。通过使用代理,客户端可以绕过浏览器的同源策略,访问任意服务器的资源。

代理有多种类型,如正向代理、反向代理、透明代理等。本文只介绍一种简单的正向代理,即客户端知道代理的存在,并主动通过代理来访问服务器。

Spring Boot中实现代理

在Spring Boot中,有多种方式来实现代理:

  • 使用RestTemplate类来发送HTTP请求,并转发响应。
  • 使用HttpClient类来创建HTTP客户端,并转发响应。
  • 使用OkHttp类来创建HTTP客户端,并转发响应。

下面给出一些示例代码:

使用RestTemplate类

假设我们有一个ProxyController类,提供了一个代理接口。我们想要通过该接口来访问其他服务器的资源。我们可以使用RestTemplate类来发送HTTP请求,并转发响应:

package com.example.demo.controller;

import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

@RestController
public class ProxyController {

    @GetMapping("/api/proxy")
    public ResponseEntity<String> proxy() {
        // 创建RestTemplate对象
        RestTemplate restTemplate = new RestTemplate();
        // 发送GET请求到目标服务器的接口,获取响应
        ResponseEntity<String> response = restTemplate.getForEntity("http://target.com/api/resource", String.class);
        // 转发响应到客户端
        return response;
    }
}
使用HttpClient类

假设我们有一个ProxyController类,提供了一个代理接口。我们想要通过该接口来访问其他服务器的资源。我们可以使用HttpClient类来创建HTTP客户端,并转发响应:

package com.example.demo.controller;

import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class ProxyController {

    @GetMapping("/api/proxy")
    public String proxy() throws Exception {
        // 创建HttpClient对象
        HttpClient httpClient = HttpClients.createDefault();
        // 创建HttpGet对象,设置请求地址
        HttpGet httpGet = new HttpGet("http://target.com/api/resource");
        // 发送GET请求到目标服务器,获取响应
        HttpResponse response = httpClient.execute(httpGet);
        // 获取响应体内容
        String content = EntityUtils.toString(response.getEntity());
        // 转发响应体内容到客户端
        return content;
    }
}
使用OkHttp类

假设我们有一个ProxyController类,提供了一个代理接口。我们想要通过该接口来访问其他服务器的资源。我们可以使用OkHttp类来创建HTTP客户端,并转发响应:

package com.example.demo.controller;

import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class ProxyController {

    @GetMapping("/api/proxy")
    public String proxy() throws Exception {
        // 创建OkHttpClient对象
        OkHttpClient okHttpClient = new OkHttpClient();
        // 创建Request对象,设置请求地址
        Request request = new Request.Builder()
                .url("http://target.com/api/resource")
                .build();
        // 发送GET请求到目标服务器,获取响应
        Response response = okHttpClient.newCall(request).execute();
        // 获取响应体内容
        String content = response.body().string();
        // 转发响应体内容到客户端
        return content;
    }
}

总结

CORS和代理都是解决跨域问题的常用方法,但是它们也有各自的优缺点。CORS是一种更简单和安全的方法,但是它需要服务器端的支持和配置。代理是一种更灵活和强大的方法,但是它需要额外的网络开销和中间层的维护。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
当前,大部分的前后端分离项目都采用Vue作为前端Spring Boot作为后端,而Vue与Spring Boot本质上是两个不同的项目,它们分别运行在不同的端口上,这就导致了跨域问题的出现。 解决跨域问题可以通过以下几种方法: 1. 在Spring Boot的Controller层添加CORS配置 在Controller层的方法上添加@CrossOrigin注解,如下所示: ``` @CrossOrigin(origins = "*", maxAge = 3600) @RestController public class ApiController { //... } ``` 其中@CrossOrigin注解中的origins参数表示允许跨域访问的域名,*表示允许所有域名访问。 2. 在Spring Boot的配置文件中添加CORS配置 在Spring Boot的配置文件中添加以下配置: ``` spring: cors: allowed-origins: "*" allowed-methods: GET, POST, PUT, DELETE, OPTIONS allowed-headers: Content-Type,X-Requested-With,accept,Origin,Access-Control-Request-Method,Access-Control-Request-Headers,Authorization exposed-headers: Access-Control-Allow-Origin,Access-Control-Allow-Credentials allow-credentials: true max-age: 3600 ``` 其中allowed-origins表示允许跨域访问的域名,*表示允许所有域名访问。 3. 在Vue项目中配置代理 在Vue项目的config/index.js文件中添加以下配置: ``` proxyTable: { '/api': { target: 'http://localhost:8080', changeOrigin: true, pathRewrite: { '^/api': '' } } } ``` 其中target表示后端接口所在的地址,pathRewrite表示将请求中的/api前缀替换为空字符串。 以上三种方法都可以解决跨域问题,具体选择哪种方法可以根据实际情况来决定。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值