Ajax跨域三(被调用方解决跨域以及spring中解决方式)

一、被调用方解决:

 Ajax跨域三(被调用方解决跨域以及spring中解决方式)

(1)服务器端实现(通过后台filter跨域的时候通过后台filter去设置允许跨域,将可以被跨域访问的请求URL和方法都添加进去

  如何判断跨域?如果存在跨域(请求头中会存在请求的url,在请求头中存在origin字段):

 Ajax跨域三(被调用方解决跨域以及spring中解决方式)

  如何实现?可以通过Filter代码实现。

  指定响应头信息(允许什么域,调用什么方法):

代码:

   在服务器端Ajaxserver1Application类中加入以下代码:

    @Bean
    public FilterRegistrationBean registrationBean(){
        FilterRegistrationBean bean = new FilterRegistrationBean();
        bean.addUrlPatterns("/*");
        bean.setFilter(new ConsFilter());
        return bean ;
    }

继承filter并实现dofilter方法:

import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.util.StringUtils;

public class ConsFilter implements Filter {
    @Override
    public void destroy() {
    }
    @Override
    public void doFilter(ServletRequest arg0, ServletResponse arg1, FilterChain arg2)
            throws IOException, ServletException {
        //响应
        HttpServletResponse res = (HttpServletResponse) arg1;
        //只开启指定的ip与端口,只开启指定的方法
        res.addHeader("Access-Control-Allow-Origin", "http://localhost:8081");
        res.addHeader("Access-Control-Allow-Methods", "GET");
        arg2.doFilter(arg0, arg1);
    }
    @Override
    public void init(FilterConfig arg0) throws ServletException {
    }
}

Ajax跨域三(被调用方解决跨域以及spring中解决方式)

允许所有域,所有方法:

代码:

//允许所有域,所有方法
res.addHeader("Access-Control-Allow-Origin", "http://localhost:8081");
res.addHeader("Access-Control-Allow-Methods", "*");

 Ajax跨域三(被调用方解决跨域以及spring中解决方式)

补充:

  是否所有请求都是先执行后判断?

  答:需要知道是简单请求非简单请求

 1)简单请求:先执行后判断

定义:
 方法为:
  GET
  HEAD
  POST
 请求header里面:
  无自定义头
  Content-Type为以下几种:
  Text/plain
  mutipart/form-data
  application/x-www-form-urlencoded

  2)非简单请求:先发出一个预检命令,然后在发出请求。先判断后执行。

  工作中常见的【非简单请求】有:

Put,delete方法的ajax请求
发送json格式的ajax请求
带自定义头的ajax请求

  当浏览器要发送跨域请求时,如果请求是复杂请求,浏览器会先发送一个options预检命令即一个options请求,当该请求通过时才会再发送真正的请求。

  该option请求会根据请求的信息去询问服务端支不支持该请求。比如发送的数据是json类型(通过content-type设置)的话,会携带一个请求头Access-Control-Request-Headers: content-type去询问支不支持该数据类型,如果支持,则请求就会通过,并发送真正的请求

 Ajax跨域三(被调用方解决跨域以及spring中解决方式)

前端报错日志:

 Ajax跨域三(被调用方解决跨域以及spring中解决方式)

查看头信息(查看预解命令信息):

 Ajax跨域三(被调用方解决跨域以及spring中解决方式)

分析:从信息头中可以查看预解命令中多个一个字段:Access-Control-Request-Headers:content-type,请求后台是否允许这个头信息,但是由于Response头中并没有给予授权该请求响应信息。

解决方案:在服务器filter中添加这个请求信息。

@Override
public void doFilter(ServletRequest arg0, ServletResponse arg1, FilterChain arg2) throws IOException, ServletException {
  HttpServletResponse res = (HttpServletResponse) arg1;
  // //只开启指定的ip与端口,只开启指定的方法
  // res.addHeader("Access-Control-Allow-Origin", "http://localhost:8081");
  // res.addHeader("Access-Control-Allow-Methods", "GET");
  //允许所有域,所有方法
  res.addHeader("Access-Control-Allow-Origin", "*");
  res.addHeader("Access-Control-Allow-Methods", "*");
  res.addHeader("Access-Control-Allow-Headers", "content-type");
  arg2.doFilter(arg0, arg1);
}

查看前端信息:

 Ajax跨域三(被调用方解决跨域以及spring中解决方式)

分析:可以看出,postJSON发出了2个请求,一个是预解命令(options),当该请求通过时才会再发送真正的POST请求

  复杂请求每次都要发送两条请求,效率很低,可以通过将预检命令缓存来减少请求设置方法是服务端响应头设置Access-Control-Max-Age,值是缓存时间(单位是秒)。

@Override
public void doFilter(ServletRequest arg0, ServletResponse arg1, FilterChain arg2) throws IOException, ServletException {
  HttpServletResponse res = (HttpServletResponse) arg1;
  // //只开启指定的ip与端口,只开启指定的方法
  // res.addHeader("Access-Control-Allow-Origin", "http://localhost:8081");
  // res.addHeader("Access-Control-Allow-Methods", "GET");
  //允许所有域,所有方法
  res.addHeader("Access-Control-Allow-Origin", "*");
  res.addHeader("Access-Control-Allow-Methods", "*");
  res.addHeader("Access-Control-Allow-Headers", "content-type");
  res.addHeader("Access-Control-Max-Age", "3600");
  arg2.doFilter(arg0, arg1);
}

Ajax跨域三(被调用方解决跨域以及spring中解决方式)

分析:第一次依然是2次请求,但是超过第二次就只有一次请求,看上面的前台信息就可以看出。

cookie跨域

问:Access-Control-Allow-Origin*,是否可以满足所有跨域请求?

 答:带有Cookie的ajax跨域 Access-Control-Allow-Origin的value不能为"*", 其值必须是当前调用方的url,否则浏览器会报错例如:

  servletResponse.addHeader("Access-Control-Allow-Origin", "http://localhost:8081")

补充:cookie的跨域(发送的cookie只能是被调用方的cookie,而不是调用方的cookie

  另外,浏览器还会报另一个错误这是只需要在响应头中设置Access-Control-Allow-Credentials的值为true

后台代码:

@GetMapping("/getCookie")
public ResultBean getCookie(@CookieValue(value="cookie") String cookie){
  System.out.println("TestController.getCookie()");
  return new ResultBean("getCookie " + cookie);
}

  注意:被调用方(服务器方)设置cookie,前端就可以设置cookiedocument.cookie="xxxx" 即可

报错:

 Ajax跨域三(被调用方解决跨域以及spring中解决方式)

  这里报错的意思是指:响应头中的信息Access-Control-Allow-Origin不能是*,必须是精确匹配,即:res.addHeader("Access-Control-Allow-Origin", "http://localhost:8081");

依旧报错:

 Ajax跨域三(被调用方解决跨域以及spring中解决方式)

  那么就在头信息中加上字段:res.addHeader("Access-Control-Allow-Credentials", "true");即可解决问题。

总结:cookie 的时候,origin必须是全匹配,不能试用*;第二就是要增加一个响应头

Cookie的跨域:

  1. "Access-Control-Allow-Credentials","true"
  2. Access-Control-Allow-Origin 不能为* ,必须为【调用方】的域名+端口
  3. 发送的Cookie是【被调用方】的cookie

  在上面的信息都不变情况下,如果浏览器中请求url127.0.0.1:8080,但是会报错(报域名错误,这里的信息中指验证localhost域名)

 问题:上面的配置有限制,如服务器url等等?如何解决可以匹配多个不同的服务器?

  解决方案:在请求中添加判断,使其通过不同的域名。

  代码:

//请求
HttpServletRequest req = (HttpServletRequest) arg0;
String origin = req.getHeader("Origin");
if(!StringUtils.isEmpty(origin)){
    res.addHeader("Access-Control-Allow-Origin", origin);
}

  带自定义头的跨域

后端代码:
@GetMapping("/getHeader")
public ResultBean getHeader(@RequestHeader("x-header1") String header1,@RequestHeader("x-header2") String header2){
  System.out.println("TestController.getHeader()");
  return new ResultBean("getHeader " + header1 + " " + header2);
}

  前端代码:

//自定义头测试代码,方式一
    it("getHeader请求", function(done) {
        // 服务器返回的结果
        var result; //初始化是undifined
        $.ajax({
            type : "get",
            url : base + "/getHeader",
            headers : {      //这里的意思是指允许cookie
                "x-header1" : "AAA" 
            },
            beforeSend : function(xhr){
                xhr.setRequestHeader("x-header2" , "BBB")
            },
            success : function(json){
                result = json;
            }
        });
        // 由于是异步请求,需要使用setTimeout来校验
        setTimeout(function() {
            expect(result).toEqual({
                "data" : "getHeader AAA BBB"
            });
            // 校验完成,通知jasmine框架
            done();
        }, 100);
    });

  前端报错,但是已经请求到自定义头信息:

 Ajax跨域三(被调用方解决跨域以及spring中解决方式)

   查看报错信息:

 Ajax跨域三(被调用方解决跨域以及spring中解决方式)

  根据报错信息,在Filter中加入以下配置即可:

res.addHeader("Access-Control-Allow-Headers", "content-type,x-header1,x-header2"); //响应头信息被写死

补充:

  针对上面写死的信息,可以动态配置。

代码:
//灵活定义响应头信息,支持所有自定义头信息
Strig header = req.getHeader("Access-Control-Request-Headers");
if(!StringUtils.isEmpty(header)){
  res.addHeader("Access-Control-Allow-Headers", header);
}

(2) NGINX配置

  首先配置本地域名信息:

 Ajax跨域三(被调用方解决跨域以及spring中解决方式)

Ajax跨域三(被调用方解决跨域以及spring中解决方式)

Nginx配置:

  首先将配置信息放在conf下,创建文件vhost,并在其下创建文件b.com.conf文件信息,在confnginx.conf下引入vhost下的所有配置信息:include vhost/*.conf;

在配置文件信息(b.com.conf:

server{
    listen 80;
    server_name b.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;
        add_header Access-Control-Allow-Origin $http_origin;
        add_header Access-Control-Allow-Headers $http_access_control_request_headers;
        #设置预解命令
        if ($request_method = OPTIONS){
           return 200;
        }
    }
}

补充(在nginx安装目下打开命令操作窗口):

  1)验证配置信息:nginx.exe -t

 Ajax跨域三(被调用方解决跨域以及spring中解决方式)

  2)启动nginxstart nginx.exe

  3)重启nginxnginx.exe -s reload

 Ajax跨域三(被调用方解决跨域以及spring中解决方式)

  停止nginxnginx.exe -s stop

调用方式:

  1)服务器端:http://b.com/test/get1

  2)客户端:http://localhost:8081

js中url基础路径:

var base = "http://b.com/test";

 

(3)APACHE配置

  参考地址:https://www.imooc.com/video/16592

(4)被调用方-spring方式配置

  Spring跨域方法:在类或者方法上添加注解——@CrossOrigin

二、调用方解决

 Ajax跨域三(被调用方解决跨域以及spring中解决方式)

1)隐藏跨域(nginx)

  aginx实现隐藏跨域:在下面的a.com.conf文件中配置参数后,在前端代码中把请求http://localhost:8081/test地址改成代理地址/ajaxserver

  反向代理:访问同一域名的的两个URL,去到两个不同的服务器

  配置信息如下:

server{
    listen 80;
    server_name a.com;
    location /{    
        proxy_pass http://localhost:8081/;
    }
    location /ajaxserver{
        proxy_pass http://localhost:8080/test/;
    }
}

  服务端:http://a.com/ajaxserver/get1

  客户端:http://a.com/

  js中url基础路径:

var base = "/ajaxserver";

补充:

  nginx中引入配置文件信息(如下在配置文件中引入vhost下所有配置信息):

Ajax跨域三(被调用方解决跨域以及spring中解决方式)

Ajax跨域三(被调用方解决跨域以及spring中解决方式)

(2)隐藏跨域(APACHE)

   参考地址:https://www.imooc.com/video/16595

  

  理论参考:http://www.cnblogs.com/lojun/articles/9426409.html

  代码下载地址:https://download.csdn.net/download/lowi313804/10584874