文档章节

跨域访问

风雪满弓刀
 风雪满弓刀
发布于 2017/03/16 19:32
字数 1996
阅读 48
收藏 0

原因:同源策略

1.JSONP

Ajax直接请求普通文件存在跨域无权限访问的问题 ,数据无法请求,但是 script 请求js确可以正常访问。

jsonp就是模仿一个 script请求 来获取数据的方式,所有基本支持的是 GET 请求

$.ajax({
   async: false,
   type: 'get',
   jsonp: "callback",//设置这个会替换浏览器发送请求时地址后面自动添加的?callback=xxx中的callback这个字,一般情况下不用传这个参数
   jsonpCallback: "callJsonP",//这个值将用来取代jQuery自动生成的随机函数名,也就是上句话中的'xxx'。
   data:自定义
   url: 'http://lnn.wuage.com:8080/pc/toJson',
   dataType: 'jsonp',
   success: function (data) {
      alert(JSON.stringify(data));
   }
});
@RequestMapping("/toJson")
@ResponseBody
public String  toJson(HttpServletRequest request,@RequestParam(value="callback")String callback)
{
   return callback + "(" + new JSONObject() + ")";
   //或者 fastJSON
   JSONObject obj=new JSONObject();
   obj.put("data","你好呀");
   JSONPObject result=new JSONPObject("callJsonP");
   result.addParameter(obj);
   String resultStr=result.toString();
   return resultStr;
}

 

2.设置 CORS协议

适用场景:承载的信息量大,get形式搞不定,需选用post传输。CORS支持所有类型的传输。

兼容性:移动端全面支持(除opera mini),PC上IE8+。

常用头

Access-Control-Allow-Origin: http://foo.org

Access-Control-Max-Age: 3628800

Access-Control-Allow-Methods: GET,PUT, DELETE

Access-Control-Allow-Headers: content-type

"Access-Control-Allow-Origin"表明它允许"http://foo.org"发起跨域请求

"Access-Control-Max-Age"表明在3628800秒内,不需要再发送预检验请求,可以缓存该结果

"Access-Control-Allow-Methods"表明它允许GET、PUT、DELETE的外域请求

"Access-Control-Allow-Headers"表明它允许跨域请求包含content-type头

1.简单请求

     只使用 GET, HEAD 或者 POST 请求方法:如果使用 POST 向服务器端传送数据,则数据类型(Content-Type)只能是 application/x-www-form-urlencoded, multipart/form-data 或 text/plain中的一种

    不会使用自定义请求头(类似于 X-Modified 这种):HTTP头部信息不超出以下{Accept,Accept-Language,Content-Language,Last-Event-ID,content-type(只限于上面提到的3种类型)}

 失败情况

     

       如果这个源不在许可范围内,会报错: No 'Access-Control-Allow-Origin' header is present on the requested resource.

      对于简单请求,浏览器直接发出CORS请求。浏览器会自动在头信息(Request Headers)中,添加一个Origin 字段,来表明本次请求来自哪个域。

      如果Origin指定的域名在许可范围内(必须是跨域了的),Response Headers中会多出几个头信息字段。

Access-Control-Allow-Credentials:true//值为true表示允许发送cookie
Access-Control-Allow-Methods:GET, POST, OPTIONS
Access-Control-Allow-Origin:http://localhost:8080
Access-Control-Max-Age:1728000

例如

withCredentials属性

因为CORS默认不发送cookie和http认证,如果要把Cookie发到服务器,就要指定Access-Control-Allow-Credentials:true;

@CrossOrigin(origins="*",allowCredentials="true")
或者
response.setHeader("Access-Control-Allow-Credentials","true");

另外AJAX中也要打开withCredentials属性。

var xhr=new XMLHttpRequest();
xhr.withCredentials=true;

jquery ajax请求参数中加入

xhrFields: {
  withCredentials: true
}

2.非简单请求

除了上面说的简单请求外都是非简单请求,比如:请求方法是PUT
或DELETE,或者Content-Type字段的类型是application/json,又或者有自定义请求头Access-Control-Request-Headers: X-Custom-Header。

比如,我添加自定义请求头

xhr.setRequestHeader('Some-Custom-Response-Header', 'value');

就会发现连续向同一地址请求了两次

第一次 options 请求

第二次请求:真实请求

这是因为浏览器发现,这是一个非简单请求,就自动发出一个"预检"请求,要求服务器确认可以这样请求。"预检"请求用的请求方法是OPTIONS,表示这个请求是用来询问的。"预检"请求之后,浏览器球会进行正常CORS请求。

3.服务器代码

方法一、 HttpServletRequest :

         在方法体内使用 HttpServletRequest 进行跨越参数设置。如果有参数,必须参数符合要求才进行  

          跨越参数设置。否则报错  400 bad requset,无返回信息

@RequestMapping("/toJson",method = RequestMethod.POST,produces="application/json; charset=utf-8")
@ResponseBody
public String  toJson(HttpServletRequest request)
{
   //设置哪些域名可以访问---如果方法有参数直接报 400 bad request 错误
   response.setHeader("Access-Control-Allow-Origin", "*");
   //解决乱码,原因 springmvc 设置 produces="application/json; charset=utf-8" 未生效
   response.setContentType( "application/json; charset=utf-8" );
   return new JSONObject();
}

方法二、 @CorssOrigin:

       使用 @CorssOrigin 在进方法体之间就对跨域参数做出设置。发生错误有返回信息。

      Controller中的action的请求方法是实际方法就可以,options方法默认支持

@CrossOrigin(origins="*")/**方法一:跨越参数有返回,有参时报错参数缺失*/
@RequestMapping(value="/toJson",method = {RequestMethod.GET},produces="application/json;charset=utf-8")
@ResponseBody
public String  toJson(HttpServletRequest request,HttpServletResponse response){
     return new JSONObject("ni你好点的");
}

方法三、基于XML的配置

<mvc:cors>
    <mvc:mapping path="/api/**"
        allowed-origins="*"
        allowed-methods="GET,POST,PUT,OPTIONS"
        allow-credentials="false"
        max-age="3600" />
</mvc:cors>

方法四、基于java代码的全局配置 (SpringMVC 4)

       支持SpringMvc 4以上  ,测试未生效

  • 在requestMapping中使用注解。
  • 全局实现 .定义类继承WebMvcConfigurerAdapter
  • 将该类注入到容器中:
@Configuration
public class WebConfig extends WebMvcConfigurerAdapter {

    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/api/**")
            .allowedOrigins("http://domain2.com")
            .allowedMethods("PUT", "DELETE")
            .allowedHeaders("header1", "header2", "header3")
            .exposedHeaders("header1", "header2")
            .allowCredentials(false).maxAge(3600);
    }
}

注入:

<bean class="com.tmall.wireless.angel.web.config.CorsConfigurerAdapter"></bean>

方法五、拦截器配置

1.使用 Spring 提供的 拦截器

<mvc:interceptors>  
   <mvc:interceptor> 
     <!--过滤的路径--> 
     <mvc:mapping path="/h5/*" />  
     <bean class="com.wuage.ossserver.web.Interceptor.CorsInterceptor">  
       <!--过滤路径中的不需要过滤的路径-->
       <property name="excludedUrls">  
          <list>  
            <value>/h5/testOrigin</value>  
          </list>  
       </property>  
     </bean>  
   </mvc:interceptor>
<mvc:interceptors>  
package com.wuage.ossserver.web.Interceptor;

import java.util.List;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

/**  
 * 请求拦截器,处理跨域问题  
 * @author 李宁宁  
 *  
 */  
public class CorsInterceptor implements HandlerInterceptor {  
  
    private List<String> excludedUrls;  
  
    public List<String> getExcludedUrls() {  
        return excludedUrls;  
    }  
  
    public void setExcludedUrls(List<String> excludedUrls) {  
        this.excludedUrls = excludedUrls;  
    }  
  
    /**  
     *   
     * 在业务处理器处理请求之前被调用 如果返回false   
     * 从当前的拦截器往回执行所有拦截器的afterCompletion(),  
     * 再退出拦截器链, 如果返回true 执行下一个拦截器,  
     * 直到所有的拦截器都执行完毕 再执行被拦截的Controller  
     * 然后进入拦截器链,  
     * 从最后一个拦截器往回执行所有的postHandle()  
     * 接着再从最后一个拦截器往回执行所有的afterCompletion()  
     *   
     * @param  request  
     *   
     * @param  response  
     */  
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response,  
            Object handler) throws Exception {  
        //* 可以替换成 特定的 网址(协议 域名 端口)
        response.setHeader("Access-Control-Allow-Origin", "*");  
        response.setHeader("Access-Control-Allow-Methods", "*");  
        response.setHeader("Access-Control-Max-Age", "3600");  
        response.setHeader("Access-Control-Allow-Headers",  
                "Origin, X-Requested-With, Content-Type, Accept");  
        response.setHeader("Access-Control-Allow-Credentials",  
                "true");
        return true;  
    }  
  
    // 在业务处理器处理请求执行完成后,生成视图之前执行的动作  
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,  
            ModelAndView modelAndView) throws Exception {  
  
    }  
  
    /**  
     *   
     * 该方法也是需要当前对应的Interceptor的preHandle方法的返回值为true时才会执行。
     * 该方法将在整个请求完成之后,也就是DispatcherServlet渲染了视图执行, 这个方法的主要作用是用于清理资源的, 
     *   
     * @param request  
     *   
     * @param response  
     *   
     * @param handler  
     *   
     */  
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response,  
            Object handler, Exception ex) throws Exception {  
  
    } 
}

2. Filter 拦截器

<filter>
   <filter-name>cros</filter-name> 
   <filter-class>com.wuage.ossserver.web.Interceptor.CORSFilter</filter-class>
</filter>
<filter-mapping>
   <filter-name>cros</filter-name>
   <url-pattern>/h5/*</url-pattern>
</filter-mapping>
public class CORSFilter implements Filter  {

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException,ServletException {
        HttpServletResponse localHttpServletResponse = (HttpServletResponse)response;
        localHttpServletResponse.addHeader("Access-Control-Allow-Origin", "*");
        localHttpServletResponse.addHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE,OPTIONS");
        localHttpServletResponse.addHeader("Access-Control-Allow-Headers", "Content-Type");
        localHttpServletResponse.addHeader("Access-Control-Max-Age", "1800");
        filterChain.doFilter(request, response);  
    }

    @Override
    public void destroy() {}

    @Override
    public void init(FilterConfig arg0) throws ServletException {}

}

注意: 使用 方法 1,2 控制器中只需要进行 特定的方法设置,无需就添加 例如 options类型的设计

        options 非简单CORS请求中 嗅探 方法类型

4.前端

<input type="file" id="file_upload"/>
<input type="button" value="上传图片" id="upload"/> 
function ajaxFileUpload(){
        var formData = new FormData();
        formData.append('file',$("#file_upload")[0].files[0]);    //将文件转成二进制形式
        $.ajax({
            type:"post",
            url:"http://localhost:8080/nitshareserver/serve/fileupload",
            async:false,
            contentType: false,    //这个一定要写
            processData: false, //这个也一定要写,不然会报错
            data:formData,
            dataType:'text',    //返回类型,有json,text,HTML。这里并没有jsonp格式,所以别妄想能用jsonp做跨域了。
            xhrFields: {withCredentials: true}, //设置是否带验证 cookie
            success:function(data){
                alert(data);
            },
            error:function(XMLHttpRequest, textStatus, errorThrown, data){
                alert(errorThrown);
            }            
        });
    }

5.补充

Springmvc模式是挂壁OPTIONS请求的,所以需要开启

<servlet>    
   <servlet-name>application</servlet-name>    
   <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>  
   <init-param>  
        <param-name>dispatchOptionsRequest</param-name>  
        <param-value>true</param-value>  
   </init-param>    
   <load-on-startup>1</load-on-startup>    
</servlet>    

 

6.常见错误:

              403 forbidden 无权限访问-有时 forbox 好使,chrom 不好使

              400 bad request  请求失败, 参数不符合要求

              301 服务器错误 或者 重定向

              405 Method Not Allowed :方法错误(复杂请求要求有 options 方法)

7.参考:

         https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Access_control_CORS

          https://my.oschina.net/wangnian/blog/689020

          http://www.tuicool.com/articles/Vf2aym

          http://blog.csdn.net/u012562943/article/details/53141991

          cookies:http://www.ruanyifeng.com/blog/2016/04/cors.html

          http://www.jianshu.com/p/7257e7c60ef5

© 著作权归作者所有

上一篇: 时间处理
风雪满弓刀
粉丝 1
博文 63
码字总数 35922
作品 0
海淀
程序员
私信 提问
关于跨域问题

跨域首先要知道域是什么?域(Domain)是Windows网络中独立运行的单位,域和域之间相互访问,则需要建立信任关系。信任关系是连接在域和域之间的桥梁。当一个域和其他域建立信任关系之后,两...

lihao0609
2017/12/13
0
0
SpringBoot 实现前后端分离的跨域访问(CORS)

一、基本介绍 CORS是一种访问机制,英文全称是Cross-Origin Resource Sharing,即我们常说的跨域资源共享,通过在服务器端设置响应头,把发起跨域的原始域名添加到Access-Control-Allow-Orig...

Jokey2017
2017/11/14
0
0
关于withCredentials和CORS[项目笔记]

参考链接 koa2-cors设置允许指定单个域名、多个域名、所有域名跨域 当一个资源从与该资源本身所在的服务器不同的域、协议或端口请求一个资源时,资源会发起一个跨域. 跨域资源共享 () 是一种...

阳光。
09/10
0
0
Java Web应用中支持跨域请求

由于工程合作开发的需要,后台的应用要能支持跨域访问,但是在这个跨域访问“时好时坏”,我们这帮屌丝所知道的就是加上两个jar包,然后声明一下Filter,感觉很简单的有没有!!感觉自己很牛...

lmy86263
2016/06/21
0
0
JQuery + JsonP 解决跨域请求

什么是跨域 跨域是指从一个域名的网页去请求另一个域名的资源。比如从www.baidu.com 页面去请求 www.google.com 的资源。跨域的严格一点的定义是:只要 协议,域名,端口有任何一个的不同,就...

刘引惟
2016/11/23
150
0

没有更多内容

加载失败,请刷新页面

加载更多

教你玩转Linux—添加批量用户

添加和删除用户对每位Linux系统管理员都是轻而易举的事,比较棘手的是如果要添加几十个、上百个甚至上千个用户时,我们不太可能还使用useradd一个一个地添加,必然要找一种简便的创建大量用户...

xiangyunyan
24分钟前
6
0
返回提示信息,如:xxx创建成功!

【服务端】在输出的方法块中,加入要输出的字段(qcm_batch_id) QCMUserType.cs: public struct QCM_Custom_Create_Batch_Out_Tag { public BASCoreType.Cmn_Out_T......

_Somuns
24分钟前
5
0
Aliyun Serverless VSCode Extension v1.12.0 发布

Aliyun Serverless VSCode Extension 是阿里云 Serverless 产品 函数计算 Function Compute 的 VSCode 插件,该插件结合了函数计算 Fun 工具以及函数计算 SDK ,是一款 VSCode 图形化开发调试...

阿里云官方博客
24分钟前
6
0
程序员如何培养解决复杂问题的能力?

今天在上网时候,突然看到了这篇文章,感觉非常的适合现在的自己去思考下,可能也适用在座的读者。程序员不仅仅是敲代码,更是一个复合能力的结合体,也不仅仅停留在技术和代码阶段。你想要成...

哥本哈根的小哥
28分钟前
8
0
市场变化驱动产品思维升级

宜信科技中心财富管理产品部负责人Bob,与大家一起聊聊个性化推荐产品功能的设计和B端产品的功能策划方式。 拓展阅读:回归架构本质,重新理解微服务 智慧金融时代,大数据和AI如何为业务赋能...

宜信技术学院
28分钟前
8
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部