解决前后端分离架构中跨域题目

打印 上一主题 下一主题

主题 1070|帖子 1070|积分 3210

前后端分离架构中,前端调用后端提供的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

    • 指明实际哀求所使用的 HTTP 方法

预检响应 Header:


  • Access-Control-Allow-Origin: http://localhost:8080

    • 指明允许访问的域。

  • Access-Control-Allow-Methods: POST

    • 指明允许的 HTTP 哀求方法。

  • 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服务器
  1. upstream resource1 {
  2.         ip_hash;
  3.         server 195.128.10.2:8989;
  4. }
  5. upstream resource2 {
  6.         ip_hash;
  7.         server  195.128.10.2:8999;
  8. }
  9. upstream resource3 {
  10.         ip_hash;
  11.         server  195.128.10.2:9999;
  12. }
  13. server
  14. {
  15.     listen       8080;
  16.     server_name localhost;
  17.    
  18.     location / {
  19.             root /home/nginx/web/dist;
  20.             index index.html index.htm;
  21.     }
  22.    
  23.         location /resource1/ {
  24.         proxy_pass http://resource1; # 转发地址
  25.         proxy_read_timeout 600s;
  26.                 proxy_set_header X-Real-IP $remote_addr;
  27.                 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
  28.                 proxy_set_header Host $host:$se;
  29.     }
  30.    
  31.         location /resource2/ {
  32.         proxy_pass http://resource2; # 转发地址
  33.         proxy_read_timeout 600s;
  34.                 proxy_set_header X-Real-IP $remote_addr;
  35.                 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
  36.                 proxy_set_header Host $host:$se;
  37.     }
  38.    
  39.     location /resource3/ {
  40.             rewrite ^/resource3(.*)$ $1 break;
  41.         proxy_pass http://resource3; # 转发地址
  42.         proxy_read_timeout 600s;
  43.                 proxy_set_header X-Real-IP $remote_addr;
  44.                 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
  45.                 proxy_set_header Host $host:$se;
  46.     }
  47. }
复制代码
(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设置响应头信息给欣赏器
  1. server {
  2.     listen       8080;
  3.     server_name  localhost;
  4.     location  /resource1  {
  5.         add_header Access-Control-Allow-Origin 'http://localhost:8080' always;
  6.         add_header Access-Control-Allow-Headers '*';
  7.         add_header Access-Control-Allow-Methods '*';
  8.         add_header Access-Control-Allow-Credentials 'true';
  9.         if ($request_method = 'OPTIONS') {
  10.             return 204;
  11.         }
  12.         proxy_pass  http://195.128.10.2:8989;
  13.     }
  14.     location  /resource2  {
  15.         add_header Access-Control-Allow-Origin 'http://localhost:8080' always;
  16.         add_header Access-Control-Allow-Headers '*';
  17.         add_header Access-Control-Allow-Methods '*';
  18.         add_header Access-Control-Allow-Credentials 'true';
  19.         if ($request_method = 'OPTIONS') {
  20.             return 204;
  21.         }
  22.         proxy_pass  http://195.128.10.2:8999;
  23.     }
  24.     location  /resource3  {
  25.         add_header Access-Control-Allow-Origin 'http://localhost:8080' always;
  26.         add_header Access-Control-Allow-Headers '*';
  27.         add_header Access-Control-Allow-Methods '*';
  28.         add_header Access-Control-Allow-Credentials 'true';
  29.         if ($request_method = 'OPTIONS') {
  30.             return 204;
  31.         }
  32.         proxy_pass  http://195.128.10.2:9999;
  33.     }
  34. }
复制代码
(3)后端解决跨域题目

   https://www.iocoder.cn/Spring-Boot/SpringMVC/?github
  通过配置文件跨域



  • 创建一个新配置文件;添加 @Configuration 注解,实现 WebMvcConfigurer 接口;重写 addCorsMappings 方法,设置允许跨域的代码。
  • 在前端使用符合 CORS 规范的网络库时,例如说 Vue 常用的网络库 axios ,在发起非简朴哀求时,会自动先先发起 OPTIONS “预检”哀求,要求服务器确认是否能够这样哀求。这样,这个哀求就会被 SpringMVC 的拦截器所处理。此时,假如我们的拦截器认为 handler 一定是 HandlerMethod 类型时,就会导致报错。
  • 解决方案:接纳过滤器,避免 OPTIONS 预查抄走到拦截器里。
  1. @Configuration
  2. public class InterceptorConfig implements WebMvcConfigurer {
  3.     @Override
  4.     public void addCorsMappings(CorsRegistry registry) {
  5.         registry.addMapping("/**")
  6.                 .allowCredentials(true)
  7.                 .allowedOriginPatterns("*")
  8.                 .allowedMethods("POST", "GET", "PUT", "OPTIONS", "DELETE")
  9.                 .allowedHeaders("*")
  10.                 .exposedHeaders("*");
  11.     }
  12. }
复制代码
通过 CorsFilter 跨域


  1. @Configuration
  2. public class CorsConfig {
  3.     private CorsConfiguration buildConfig() {
  4.         CorsConfiguration corsConfiguration = new CorsConfiguration();
  5.         corsConfiguration.addAllowedOrigin("*");
  6.         corsConfiguration.addAllowedHeader("*");
  7.         corsConfiguration.addAllowedMethod("*");
  8.         corsConfiguration.setAllowCredentials(true);
  9.         corsConfiguration.setMaxAge(3600L);
  10.         return corsConfiguration;
  11.     }
  12.    
  13.         @Bean
  14.     public CorsFilter corsFilter() {
  15.         UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
  16.         source.registerCorsConfiguration("/**", buildConfig());
  17.         return new CorsFilter(source);
  18.     }   
  19. }
复制代码
ResponseBodyAdvice

通过重写 ResponseBodyAdvice 接口中的 beforeBodyWrite(返回之前重写)方法,可以对所有的接口进行跨域设置
  1. @ControllerAdvice
  2. public class ResponseAdvice implements ResponseBodyAdvice {
  3.     /**
  4.      * 内容是否需要重写(通过此方法可以选择性部分控制器和方法进行重写)
  5.      * 返回 true 表示重写
  6.      */
  7.     @Override
  8.     public boolean supports(MethodParameter returnType, Class converterType) {
  9.         return true;
  10.     }
  11.     /**
  12.      * 方法返回之前调用此方法
  13.      */
  14.     @Override
  15.     public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType,
  16.                                   Class selectedConverterType, ServerHttpRequest request,
  17.                                   ServerHttpResponse response) {
  18.         // 设置跨域
  19.         response.getHeaders().set("Access-Control-Allow-Origin", "*");
  20.         return body;
  21.     }
  22. }
复制代码
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

勿忘初心做自己

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表