请求跨越问题三个解决方案

一.什么算跨域

我们先回顾一下域名地址的组成: 
http://www.baidu.com:80/script/ajax.js 
其中http://(协议号) 
www(子域名) 
google(主域名) 
80(端口) 
script/ajax.js (请求的地址) 
当协议、子域名、主域名、端口号中任意一各不相同时,都算不同的“域”。不同的域之间相互请求资源,就叫“跨域”。 (自参考自)

二.跨域出现的原因

这个是浏览器做得限制,因为如果用跨域访问会造成不安全,可以不进行CSRF攻击(有关介绍](https://www.zhihu.com/question/26379635))

三.解决办法

目前有三种解决办法:

  1. 用代理服务器代理
  2. 使用jsonp
  3. 服务端设置Request Header头中Access-Control-Allow-Origin为指定可获取数据的域名

详细用法:

1.使用代理服务器:

原理:可以先使用自己的服务器用http请求访问其他服务器的资源,前端用ajax来访自己的服务器。

2.使用JSONP

原理:在同源策略下,在某个服务器下的页面是无法获取到该服务器以外的数据的,但img、iframe、script等标签是个例外,这些标签可以通过src属性请求到其他服务器上的数据。利用script标签的开放策略,我们可以实现跨域请求数据,当然,也需要服务端的配合。当我们正常地请求一个JSON数据的时候,服务端返回的是一串JSON类型的数据,而我们使用JSONP模式来请求数据的时候,服务端返回的是一段可执行的JavaScript代码。 
举个例子假设从服务器(http://www.a.com/user?id=123)获取一个数据如下:

{"id": 123, "name" : 张三, "age": 17}
  • 1
  • 1

那么,使用JSONP方式请求(http://www.a.com/user?id=123&callback=foo)的数据将会是如下:

foo({"id": 123, "name" : 张三, "age": 17});
  • 1
  • 1

当然,如果服务端考虑得更加充分,返回的数据可能如下:

try{foo({"id": 123, "name" : 张三, "age": 17});}catch(e){}
  • 1
  • 1

这时候我们只要定义一个foo()函数,并动态地创建一个script标签,使其的src属性为http://www.a.com/user?id=123&callback=foo

做法:在jQuery中如何通过JSONP来跨域获取数据 : 
第一种方法是在ajax函数中设置dataType为’jsonp’:

$.ajax({
        dataType: 'jsonp',
        url: 'http://www.a.com/user?id=123',
        success: function(data){
                //处理data数据
        }
});
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

第二种方法是利用getJSON来实现,只要在地址中加上callback=?参数即可:

$.getJSON('http://www.a.com/user?id=123&callback=?', function(data){
        //处理data数据
});
或者
//此时也可以在函数外定义foo方法
function foo(data){
        //处理data数据
}
$.getJSON('http://www.a.com/user?id=123&callback=foo');
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

JSONP的应用 
JSONP在开放API中可以起到非常重要的作用,开放API是运用在开发者自己的应用上,而许多应用往往是在开发者的服务器上而不是在新浪微博的服务器上,因此跨域请求数据成为开发者们所需要解决的一大问题,广大开放平台应该实现对JSONP的支持,这一点新浪微博开放平台便做的非常好(虽然某些API里没有说明,但实际上是可以使用JSONP方式调用的)。 
参考自 
缺点:JSONP方法是一种非官方方法,而且这种方法只支持GET方式,不如POST方式安全。 
即使使用jQuery的jsonp方法,type设为POST,也会自动变为GET

3.服务端设置Response Header头中Access-Control-Allow-Origin为指定可获取数据的域名

原理:通过自己的服务器响应首部字段加上一下响应字段

一.方法:

  1. 服务端设置Respone Header头中Access-Control-Allow-Origin
  2. 配合前台使用jsonp
  3. 继承WebMvcConfigurerAdapter 添加配置类

二.实例:

1.前端:因为我们用了前后端分离,前端用node服务器,node服务器再用了ajax反向代理请求到我的spring boot 服务器。其中node服务器也用了ajax发出请求所以也存在跨域的问题。具体代码:

 app.all(apiRoot + '/*', proxy('127.0.0.1:' + proxyPort, {
    forwardPath: function(req, res) {
      console.log('req: ', req, 'res; ', res);
      return require('url').parse(req.url).path;
    }
  }));
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

后台(用的是spring boot 1.3.7.RELEASE) :用了一个filter进行了身份验证同时进行了跨域处理,具体代码:

public class AuthFilter implements Filter {
    //    @Autowired
    //这个不能自动注入servlet和filter是被tomcat管理的
    private BaseUserService baseUserService;
    private String[] excludePaths;

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        System.out.println("initFilter");
        //不能在初始化中通过Appliaction Context获取因为这时候还没初始化Application Context
        //baseUserService = SpringUtils.getBean("baseUserService", BaseUserService.class);
        excludePaths = new String[]{"/api/user/noLogin", "/api/user/tokenError", "/api/user/loginForeground",
                "/api/user/loginBackground", "/api/user/inCorrectUserId"};
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest httpServletRequest = (HttpServletRequest) request;
        HttpServletResponse httpServletResponse = (HttpServletResponse) response;
        //这里填写你允许进行跨域的主机ip
        httpServletResponse.setHeader("Access-Control-Allow-Origin", "*");
        //允许的访问方法
        httpServletResponse.setHeader("Access-Control-Allow-Methods", "POST, GET, PUT, OPTIONS, DELETE, PATCH");
        //Access-Control-Max-Age 用于 CORS 相关配置的缓存
        httpServletResponse.setHeader("Access-Control-Max-Age", "3600");
        httpServletResponse.setHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
        String userId = request.getParameter("userId");
        String token = request.getParameter("token");
        //有token的                                                     `
        if (userId != null && token != null) {
            try {
                Integer id = Integer.parseInt(userId);
                if (baseUserService == null)
                    baseUserService = SpringUtils.getBean("baseUserService", BaseUserService.class);
                int status = baseUserService.checkLogin(id, token);
                if (status == 1) {
                    chain.doFilter(request, response);
                } else if (status == 0) {
                    httpServletResponse.sendRedirect("/api/user/tokenError");
                } else if (status == -2) {
                    httpServletResponse.sendRedirect("/api/user/inCorrectUserId");
                } else {
                    httpServletResponse.sendRedirect("/api/user/noLogin");
                }
            } catch (NumberFormatException exception) {
                httpServletResponse.sendRedirect("/api/user/inCorrectUserId");
            }
        } else {
            String path = httpServletRequest.getServletPath();
            if (excludePath(path)) {
                chain.doFilter(request, response);
            } else {
                httpServletRequest.getRequestDispatcher("/api/user/noLogin").forward(request, response);
            }
        }
//        ((HttpServletResponse) response).addHeader("Access-Control-Allow-Origin", "*");
//        CorsFilter corsFilter=new CorsFilter();

    }

    private boolean excludePath(String path) {
        for (int i = 0; i < excludePaths.length; i++) {
            if (path.equals(excludePaths[i]))
                return true;
        }
        return false;
    }

    @Override
    public void destroy() {
        System.out.println("destroy method");
    }

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75

这种方法还适用于servlet中,特别注意的是一定要在filter动作之前加上这句话,也就是在代码的最前面加上这个话。 
跨域资源共享 CORS 详解(相关链接) 
2.详细请看(点开) 
3.具体代码:

package edu.ecnu.yjsy.conf;  

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

@Configuration  
public class CorsConfig extends WebMvcConfigurerAdapter {  

    @Override  
    public void addCorsMappings(CorsRegistry registry) {  
        registry.addMapping("/**")  
                .allowedOrigins("*")  
                .allowCredentials(true)  
                .allowedMethods("GET", "POST", "DELETE", "PUT")  
                .maxAge(3600);  
    }  

}  
转自http://blog.csdn.net/hanghangde/article/details/53946207

你可能感兴趣的:(请求跨越问题三个解决方案)