http跨域

请求跨域

一、为什么会发生跨域:
  1. 浏览器限制
  2. 跨域(协议,服务器ip,端口不一样)
  3. XHR(XMLHTTPRequest)请求
    1. 像图片<img src=“url”/> 发送的请求type为json,就不是xhr请求,所以不会发生跨域问题
    2. 一般type为xhr会发生跨域请求问题
二、解决思路:
  1. 浏览器限制:
    1. chrome.exe启动是加入参数:–disable-web-security
  2. JSONP解决(动态创建一个 script 实现发送请求):
    <script>
    	$.ajax({
            url: "/url",
            dataType: "jsonp",
            // url后面拼接的参数名称
            jsonp: "callback",
            // url后面有个参数名为  _  ,为了防止浏览器缓存,将cache设置为true表示允许浏览器缓存该请求
            cache: true,
            success: function (res) {
                console.log(res)
            }
        });
    </script>
    
    1. jsonp是什么:

      JSONP(JSON with Padding)是资料格式 JSON 的一种“使用模式”,可以让网页从别的网域要资料。由于同源策略,一般来说位于 server1.example.com 的网页无法与不是 server1.example.com 的服务器沟通,而 HTML 的<script> 元素是一个例外。利用<script> 元素的这个开放策略,网页可以得到从其他来源动态产生的 JSON 资料,而这种使用模式就是所谓的 JSONP。用 JSONP 抓到的资料并不是 JSON,而是任意的 JavaScript,用 JavaScript 直译器执行而不是用 JSON 解析器解析。

    2. 使用jsonp后台代码需要改动嘛?

      需要,jsonp把返回来的对象当成js代码来解析,肯定有问题!

      SpringBoot老版本处理方式

      @ControllerAdvice
      public class JsonoAdvice implements  AbstractJsonpResponseBodyAdvice {
          public JsonpAdvice() {
              super("callback");
          }
      }
      

      目前该方法以被废弃;不管用什么方法,jsonp请求返回结果是一个js方法(格式如下)

      // 方法名为url的callback参数
      callback({
          "message":"成功",
          ……
      })
      

      可以自由发挥,例如利用拦截器Filter,controller方法中通过PrintWriter写到页面

      @RequestMapping(value = "/url")
      public void getGroupById(@RequestParam("callback") Long callback,
      HttpServletRequest request, HttpServletResponse response)
      throws IOException {
      	response.setContentType("text/html");
      	response.setCharacterEncoding("utf-8");
      	PrintWriter out = response.getWriter();
      	out.print(callback + "({\"message\":\"成功\"})");
      }
      
    3. JSONP实现原理:

      1. 发送的请求type是script
      2. 返回类型是 js 脚本
      3. 请求路径加了:callback参数,后台接受到参数,将返回结果设置成js脚本。返回js脚本是以callback参数为方法名的js方法。
    4. JSONP弊端:

      1. 服务端需要修改代码
      2. 只支持get请求
      3. 发送的不是XHR请求(XHR支持异步,各种事件,而JSONP不行)
  3. 解决请求跨域:
    1. 被调用方解决:

      ​ 浏览器实现执行还是先判断?

      ​ 先执行

      ​ 如何判断?

      ​ 跨域请求,请求头多一个Origin字段,存放跨域请求地址;在返回值中检查有没有该字段,没有就 会报错

      ​ 预检命令:

      ​ 复杂的请求(POST……),需要先通过预检命令 OPTIONS ;第二次发送真正的请求。通过在响 应头设置Access-Control-Max-Age属性,设置预检命令缓存

      1. 服务器端实现

        1. Filter方法实现

          public class TestCorsFilter implements Filter {
              @Override
              public void init(FilterConfig filterConfig) throws ServletException {
          
              }
              @Override
              public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
                  HttpServletResponse resp = (HttpServletResponse) response;
                  HttpServletRequest req = (HttpServletRequest) request;
                  
                  String origin = req.getHeader("Origin");
                  if (!StringUtils.isEmpty(origin.trim())){
                      //这样写满足所有域跨域问题
                      resp.setHeader("Access-Control-Allow-Origin",origin);
                  }
                  //设置跨域请求地址(设置为 * 时,不能发送带cookie跨域请求,必须全匹配)
                  //resp.setHeader("Access-Control-Allow-Origin","*");
                  
                  String headers = req.getHeader("Access-Control-Allow-Headers");
                  if (!StringUtils.isEmpty(origin.trim())){
                      //这样写满足所有请求头跨域问题
                      resp.setHeader("Access-Control-Allow-Headers",headers);
                  }
                  
                  //处理非简单请求(类似于Post)
                  //resp.setHeader("Access-Control-Allow-Headers","Content-Type");
          
                  //设置可以跨域请求方法
                  resp.setHeader("Access-Control-Allow-Methods","*");
                  
                  //设置浏览器缓存非简单请求预检命令(类似于Post)3600 单位 S
                  resp.setHeader("Access-Control-Max-Age","3600");
                  
                  //设置发送带cookie请求(cookie放在被调用方)
                  resp.setHeader("Access-Control-Allow-Credentials","true");
          
                  chain.doFilter(request,resp);
              }
              @Override
              public void destroy() {
          
              }
          }
          
          @Bean
          public FilterRegistrationBean registrationBean(){
              FilterRegistrationBean bean = new FilterRegistrationBean();
              bean.addUrlPatterns("/*");
              bean.setFilter(new TestCorsFilter());
              return bean;
          }
          

          设置Cookie小技巧:

          //服务端
          @GetMapping("/getCookie")
          public String getCookie(@CookieValue(value="cookie")String cookie){
              return cookie;
          }
          //浏览器设置cookie
          docment.cookie = "cookie=fzz"
          //ajax请求 增加该字段,设置跨域请求带cookie
          xhrFields:{
              withCredentials:true
          }
          
        2. @CrossOrigin实现

          @RestController
          @RequestMapping("/corsTest")
          public class TestCorsController {
              @RequestMapping(value = "/test")
              //	使用该注解,可以设置整个类,或方法,可以设置跨域相关属性,例如:能跨域的域名地址
              @CrossOrigin
              public String test(){
                  return "hello world!";
              }
          }
          
        3. MVC拦截器

          @Configuration
          @EnableWebMvc
          public class CorsConfig implements WebMvcConfigurer {
              @Override
              public void addCorsMappings(CorsRegistry registry) {
                  //设置允许跨域的路径
                  registry.addMapping("/**")
                          //设置允许跨域请求的域名
                          .allowedOrigins("*")
                          //是否允许证书 不再默认开启
                          .allowCredentials(true)
                          //设置允许的方法
                          .allowedMethods("*")
                          //跨域允许时间
                          .maxAge(1000);
              }
          }
          
      2. nginx配置

        增加域名映射nginx配置文件,放在vhost目录下,以 .conf 结尾:

        server{
        	#监听的端口
        	listen 80;
        	#监听的域名
        	server_name fzz.com;
        	
        	#映射的地址
        	location /{
        		proxy_pass http://localhost:8080;
        		
        		#增加请求头
        		add_header Access-Control-Allow-Methods *;
        		add_header Access-Control-Max-Age 3600;
        		add_header Access-Control-Allow-Credentials true;
        		
        		#从request中获取数据添加到请求头
        		add_header Access-Control-Allow-Origin $http_origin;
        		add_header Access-Control-Allow-Headers $http_access_control_request_headers;
        		
        		#若是做预检,直接返回200
        		if ($request_method = OPTIONS){
        			return 200;
        		}
        	}
        }
        

        需要在nginx.conf中添加 include vhost/*.conf;

        测试配置文件是否正确:nginx.exe -t

        启动nginx:start nginx.exe

        重启nginx:nginx.exe -s reload

        停止nginx:nginx.exe -s stop

      3. Apache配置

        在Apache conf/httpd.conf配置文件中打开如下模块:

        1. 打开Apache虚拟主机打开:LoadModule vhost_alias_module modules/mod_vhost_alias.so
        2. 打开配置文件:Include conf/extra/httpd-vhosts.conf
        3. 打开proxy模块打开:LoadModule proxy_module modules/mod_proxy.so
        4. 打开proxy_http模块:LoadModule proxy_http_module modules/mod_proxy_http.so
        5. 打开mod_headers模块:LoadModule headers_module modules/mod_headers.so
        6. 打开mod_rewrite模块:LoadModule rewrite_module modules/mod_rewrite.so

        在conf/extra/httpd-vhosts.conf 文件中添加如下配置:

        <VirtualHost *:80>
            ServerName fzz.com
            ErrorLog "logs/fzz.com-error.log"
            CustomLog "logs/fzz.com-access.log" common
        	ProxyPass / http://localhost:8080/
        	
        	#把请求头的origin值返回到Access-Control-Allow-Origin字段
        	Header always set Access-Control-Allow-Origin "expr=%{req:origin}"
        	
        	#把请求头的Access-Control-Request-Headers值返回到Access-Control-Allow-Headers字段
        	Header always set Access-Control-Allow-Headers "expr=%{req:Access-Control-Request-Headers}"
        	
        	Header always set Access-Control-Allow-Methods "*"
        	Header always set Access-Control-Allow-Credentials "true"
        	Header always set Access-Control-Max-Age "3600"
        
        	#处理预检命令OPTIONS,直接返回204
        	RewriteEngine On
        	RewriteCond %{REQUEST_METHOD} OPTIONS
        	RewriteRule ^(.*)$ "/" [R=204,L]
        
        </VirtualHost>
        

        利用:httpd.exe -t检测文件是否配置错误

  4. 调用方隐藏跨域

    浏览器发送服务直接到nginx服务器上 例如:/ajaxserver

    nginx配置:

    location /ajaxserver{
    	proxy_pass http://localhost:8080/test/;
    }
    

    浏览器并没有发现域名变化,不会形成跨域问题。

    Apache配置:

    <VirtualHost *:80>
        ServerName fzz.com
        ErrorLog "logs/fzz.com-error.log"
        CustomLog "logs/fzz.com-access.log" common
        ProxyPass /ajaxserver http://localhost:8080/test
    	ProxyPass / http://localhost:8080/
    </VirtualHost>
    
  5. gateway跨域配置
gateway:
      globalcors:
        corsConfigurations:
          '[/**]':
            allowedOrigins: "*"
            allowedMethods: "*"
            allowedHeaders: "*"
            maxAge: 3600
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值