前后端分离架构中,前端调用后端提供的API接口来获取数据。由于欣赏器的 同源策略要求当前哀求与目标哀求的域名、协议、端口都要相同,而前端服务与后端服务往往会被部署到不同的机器,不同端口上,因此会产生跨域题目。
1. 为什么产生跨域题目
(1)同源策略
同源策略是由 Netscape 提出的一个著名的安全策略,它是欣赏器最焦点也是最基本的安全功能,所有支持 JavaScript 的欣赏器都会使用这个策略。在同源策略中,要求当前哀求与目标哀求的域名、协议、端口都要相同。 同源策略详细规则如下表:
(2)非跨域哀求与跨域哀求
非跨域哀求,在哀求头中会只包罗哀求的主机名。
跨域哀求,在哀求头中会既包罗要哀求的主机名还包括当前的源主机名。
2.欣赏器对哀求的分类
在 HTTP1.1 协议中,哀求方法分为 GET、POST、PUT、DELETE、HEAD、TRACE、OPTIONS、CONNECT 八种。欣赏器根据哀求方法和哀求类型将哀求划分为 简朴哀求 和 非简朴哀求。
(1)简朴哀求:欣赏器先发送哀求再判断是否跨域
- 哀求方法为 GET、POST、HEAD
- Request Headers 中无自界说的哀求头信息
- Content-Type 为 text/plain、multipart/form-data、application/x-www-form-urlencoded
(2)非简朴哀求:欣赏器先发送预检下令(OPTIONS 哀求方法),查抄通过后才发送真正的数据哀求
预检哀求 Headers:
- Access-Control-Request-Headers: content-type,x-token
- Access-Control-Request-Method: POST
预检响应 Header:
- Access-Control-Allow-Origin: http://localhost:8080
- Access-Control-Allow-Methods: POST
- Access-Control-Allow-Headers: content-type, x-token
- Access-Control-Max-Age: 3600
- 指明该响应的有效时间,在有效时间内,欣赏器无须为同一哀求再次发起预检哀求。欣赏器查抄预检响应信息,假如预检通过就发送实际哀求。使用预检哀求可以避免跨域哀求对服务器的数据产生未预期的影响。
哀求方法为 PUT、DELETE 的 AJAX 哀求、发送 JSON 格式的 AJAX 哀求、带自界说头的 AJAX 哀求都好坏简朴哀求。
3.解决跨域题目
- Nginx部署: 195.128.10.1:8080
- 前端服务部署: 195.128.10.1:9528
- 后端服务1部署: 195.128.10.2:8989
- 后端服务2部署: 195.128.10.2:8999
- 后端服务3部署: 195.128.10.2:9999
(1)nginx中配置地址转发
- 用户访问 Nginx 地址 http://195.128.10.1:8080,前端访问的后端地址为http://195.128.10.1:8080/resource1 ,http://195.128.10.1:8080/resource2,http://195.128.10.1:8080/resource3。Nginx 根据 url转发。
注意:proxy_set_header 是 nginx 设置哀求头给上游server服务器
- upstream resource1 {
- ip_hash;
- server 195.128.10.2:8989;
- }
- upstream resource2 {
- ip_hash;
- server 195.128.10.2:8999;
- }
- upstream resource3 {
- ip_hash;
- server 195.128.10.2:9999;
- }
- server
- {
- listen 8080;
- server_name localhost;
-
- location / {
- root /home/nginx/web/dist;
- index index.html index.htm;
- }
-
- location /resource1/ {
- proxy_pass http://resource1; # 转发地址
- proxy_read_timeout 600s;
- proxy_set_header X-Real-IP $remote_addr;
- proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
- proxy_set_header Host $host:$se;
- }
-
- location /resource2/ {
- proxy_pass http://resource2; # 转发地址
- proxy_read_timeout 600s;
- proxy_set_header X-Real-IP $remote_addr;
- proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
- proxy_set_header Host $host:$se;
- }
-
- location /resource3/ {
- rewrite ^/resource3(.*)$ $1 break;
- proxy_pass http://resource3; # 转发地址
- proxy_read_timeout 600s;
- proxy_set_header X-Real-IP $remote_addr;
- proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
- proxy_set_header Host $host:$se;
- }
- }
复制代码 (2)nginx中添加允许跨域哀求头
- 用户访问前端地址 http://195.128.10.1:9528,前端访问 Nginx 地址http://195.128.10.1:8080/resource1 ,http://195.128.10.1:8080/resource2,http://195.128.10.1:8080/resource3,Nginx 根据 URL 转发。
注意:add_header是nginx设置响应头信息给欣赏器
- server {
- listen 8080;
- server_name localhost;
- location /resource1 {
- add_header Access-Control-Allow-Origin 'http://localhost:8080' always;
- add_header Access-Control-Allow-Headers '*';
- add_header Access-Control-Allow-Methods '*';
- add_header Access-Control-Allow-Credentials 'true';
- if ($request_method = 'OPTIONS') {
- return 204;
- }
- proxy_pass http://195.128.10.2:8989;
- }
- location /resource2 {
- add_header Access-Control-Allow-Origin 'http://localhost:8080' always;
- add_header Access-Control-Allow-Headers '*';
- add_header Access-Control-Allow-Methods '*';
- add_header Access-Control-Allow-Credentials 'true';
- if ($request_method = 'OPTIONS') {
- return 204;
- }
- proxy_pass http://195.128.10.2:8999;
- }
- location /resource3 {
- add_header Access-Control-Allow-Origin 'http://localhost:8080' always;
- add_header Access-Control-Allow-Headers '*';
- add_header Access-Control-Allow-Methods '*';
- add_header Access-Control-Allow-Credentials 'true';
- if ($request_method = 'OPTIONS') {
- return 204;
- }
- proxy_pass http://195.128.10.2:9999;
- }
- }
复制代码 (3)后端解决跨域题目
https://www.iocoder.cn/Spring-Boot/SpringMVC/?github
通过配置文件跨域
- 创建一个新配置文件;添加 @Configuration 注解,实现 WebMvcConfigurer 接口;重写 addCorsMappings 方法,设置允许跨域的代码。
- 在前端使用符合 CORS 规范的网络库时,例如说 Vue 常用的网络库 axios ,在发起非简朴哀求时,会自动先先发起 OPTIONS “预检”哀求,要求服务器确认是否能够这样哀求。这样,这个哀求就会被 SpringMVC 的拦截器所处理。此时,假如我们的拦截器认为 handler 一定是 HandlerMethod 类型时,就会导致报错。
- 解决方案:接纳过滤器,避免 OPTIONS 预查抄走到拦截器里。
- @Configuration
- public class InterceptorConfig implements WebMvcConfigurer {
- @Override
- public void addCorsMappings(CorsRegistry registry) {
- registry.addMapping("/**")
- .allowCredentials(true)
- .allowedOriginPatterns("*")
- .allowedMethods("POST", "GET", "PUT", "OPTIONS", "DELETE")
- .allowedHeaders("*")
- .exposedHeaders("*");
- }
- }
复制代码 通过 CorsFilter 跨域
- @Configuration
- public class CorsConfig {
- private CorsConfiguration buildConfig() {
- CorsConfiguration corsConfiguration = new CorsConfiguration();
- corsConfiguration.addAllowedOrigin("*");
- corsConfiguration.addAllowedHeader("*");
- corsConfiguration.addAllowedMethod("*");
- corsConfiguration.setAllowCredentials(true);
- corsConfiguration.setMaxAge(3600L);
- return corsConfiguration;
- }
-
- @Bean
- public CorsFilter corsFilter() {
- UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
- source.registerCorsConfiguration("/**", buildConfig());
- return new CorsFilter(source);
- }
- }
复制代码 ResponseBodyAdvice
通过重写 ResponseBodyAdvice 接口中的 beforeBodyWrite(返回之前重写)方法,可以对所有的接口进行跨域设置
- @ControllerAdvice
- public class ResponseAdvice implements ResponseBodyAdvice {
- /**
- * 内容是否需要重写(通过此方法可以选择性部分控制器和方法进行重写)
- * 返回 true 表示重写
- */
- @Override
- public boolean supports(MethodParameter returnType, Class converterType) {
- return true;
- }
- /**
- * 方法返回之前调用此方法
- */
- @Override
- public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType,
- Class selectedConverterType, ServerHttpRequest request,
- ServerHttpResponse response) {
- // 设置跨域
- response.getHeaders().set("Access-Control-Allow-Origin", "*");
- return body;
- }
- }
复制代码 免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |