日期:2019年5月18日

事情原因:由于微信小程序的开发只有测试环境,而后台提供接口的环境是开发环境;两个环境的域名不同,导致前端开发产生了跨域问题;

理论概念:

  1、同源策略:同源策略是浏览器的安全基石,存储在浏览器中的数据(cookie)必须在同域(相同域名)下才能任意的读取,而非同域下的浏览器资源是不允许任意操作的。即同源策略下,非同源的网站之间不能发送Ajax请求;如果没有同源策略,会导致浏览器中的数据可以被任何来源请求进行访问与资源操作,存储在浏览器中的数据就没有任何安全可言;

2、域名:相同域名(www.baidu .com;所有的请求都来自于这个域名下,即为同域下);

不同域名(www.baidu.com,www.google.com;来自google的请求不能直接操作baidu域下的资源);

3、同源:两个页面的【协议】、【端口(如果指定了)】、【主机】都相同,则这两个页面具有相同的源。有一个元素不同,则是不同源的;

  4、跨域资源共享(CORS):

1)它是一份浏览器技术的规范,提供Web服务从不同域传来沙盒脚本的方法,泳衣避开浏览器的同源策略,JSONP模式的现代版(相关JSONP的知识请自己查阅,本文不再做解释说明);

     2)实现思路是使用自定义的HTTP头部制定一个可以互相认可的约定(例如浏览器给服务器传送一个头信息A,服务器给浏览器传送一个头信息B,就可以不受同源策略的限制),从而决定请求与响应成功与否;

     3)CORS不关心安全问题,只解决资源共享的问题;安全性可以从请求时效性,token验证,IP验证,来源验证等方面考虑安全问题;

  5、CORS与JSONP的区别:

1)JSONP只能实现GET请求,而CORS支持所有的请求;

      2)使用CORS,前端开发者可以使用普通的XMLHttpRequest发起的请求和响应的数据,比JSONP有更好的错误处理;

      3)JSONP主要被版本比较老的浏览器支持,这些老版本的浏览器往往不支持CORS(IE6,IE7,Open mini),而绝大多数现代浏览器都已经支持了CORS;

  6、注:在跨域请求中,请求是可以发送到服务器的,服务器也可以响应数据,但服务器响应到浏览器的数据,浏览器拒绝解析(不太确定是拒绝接受还是拒绝解析?),导致的资源无法共享;

解决方案

只要解决跨域资源共享即可,CROS解决方案主要有以下几种:

  1、自定义CORSFilter(Intercepter):适用于设置单一的(全部)授权访问,所有配置固定,操作简单,不根据请求类型做不同的处理,粒度比较大;

    

 @Bean
public FilterRegistrationBean corsFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration config = new CorsConfiguration();
config.setAllowCredentials(true); config.addAllowedOrigin("http://localhost:9000");
config.addAllowedOrigin("null");
config.addAllowedHeader("*");
config.addAllowedMethod("*");
source.registerCorsConfiguration("/**", config); // CORS 配置对所有接口都有效
FilterRegistrationBean bean = newFilterRegistrationBean(new CorsFilter(source));
bean.setOrder(0);
return bean;
}

  2、Nginx代理配置(配置在location中)

    

 #
# Wide-open CORS config for nginx
#
location / {
if ($request_method = 'OPTIONS') {
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
#
# Custom headers and headers various browsers *should* be OK with but aren't
#
add_header 'Access-Control-Allow-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type';
#
# Tell client that this pre-flight info is valid for 20 days
#
add_header 'Access-Control-Max-Age' 1728000;
add_header 'Content-Type' 'text/plain charset=UTF-8';
add_header 'Content-Length' 0;
return 204;
}
if ($request_method = 'POST') {
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
add_header 'Access-Control-Allow-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type';
}
if ($request_method = 'GET') {
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
add_header 'Access-Control-Allow-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type';
}
}

(细粒度配置)

    

 #js跨域支持
#add_header 'Access-Control-Allow-Origin' '*';
#add_header 'Access-Control-Allow-Credentials' 'true';
#add_header 'Access-Control-Allow-Headers' 'Authorization,Content-Type,Accept,Origin,User-Agent,DNT,Cache-Control,X-Mx-ReqToken,X-Requested-With';
#add_header 'Access-Control-Allow-Methods' 'GET,POST,OPTIONS';

(粗粒度配置)

  3、使用spring框架提供的注解@CrossOrigin

    1)第一种情况,对接口的粒度进行配置    

    

 @CrossOrigin(origins = {"http://localhost:9000", "null"})
@RequestMapping(value = "/test", method = RequestMethod.GET)
public String greetings() {
return "{\"project\":\"just a test\"}";
}

(接口)

    2)第二种情况,对类的粒度进行配置  

    

 @CrossOrigin(origins = {"http://localhost:9000", "null"})
@RestController
@SpringBootApplication
public class SpringBootCorsTestApplication {
// xxx
}

  4、全局配置

    

 @Configuration
public class WebConfig extends WebMvcConfigurerAdapter { @Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("http://localhost:9000", "null")
.allowedMethods("POST", "GET", "PUT", "OPTIONS", "DELETE")
.maxAge(3600)
.allowCredentials(true);
}
}

  5、支持多域名配置的CORSFilter

    因为知道已经有可以用的库可以解决,所以就没重复造轮子了。其实因为懒,看看别人的源码算了.在mvnrepository搜索cors-filter,目前也就两个可以用

    org.ebaysf.web 的 cors-filter,项目地址:https://github.com/ebay/cors-filter

com.thetransactioncompany的 cors-filter,项目地址:http://software.dzhuvinov.com/cors-filter.htm

-------------------------------------------------------------------------------------------------

Ps:与前端的交互:

  非简单请求的跨源请求,浏览器会在真实请求发出前,增加一次 OPTION 请求,称为预检请求( preflight request )。预检请求将真实请求的信息,包括请求方法、自定义头字段、源信息添加到 HTTP 头信息字段中,询问服务器是否允许这样的操作。

  比如对于 DELETE 请求:

                      OPTIONS /test HTTP/1.1
   Origin: http://www.examples.com
   Access-Control-Request-Method: DELETE
   Access-Control-Request-Headers: X-Custom-Header
   Host: www.examples.com

  与 CORS 相关的字段有:

                     Access-Control-Request-Method: 真实请求使用的 HTTP 方法;

                     Access-Control-Request-Headers: 真实请求中包含的自定义头字段。

  服务器收到请求时,需要分别对 Origin 、 Access-Control-Request-Method 、 Access-Control-Request-Headers 进行验证,验证通过后,会在返回 Http 头信息中添加

                    Access-Control-Allow-Origin: http://www.examples.com
     Access-Control-Allow-Methods: GET, POST, PUT, DELETE
     Access-Control-Allow-Headers: X-Custom-Header
     Access-Control-Allow-Credentials: true
     Access-Control-Max-Age: 1728000

  他们的含义分别是:

  1. Access-Control-Allow-Methods: 真实请求允许的方法
  2. Access-Control-Allow-Headers: 服务器允许使用的字段
  3. Access-Control-Allow-Credentials: 是否允许用户发送、处理 cookie
  4. Access-Control-Max-Age: 预检请求的有效期,单位为秒。有效期内,不会重复发送预检请求

当预检请求通过后,浏览器会发送真实请求到服务器。这就实现了跨源请求

-------------------------------------------------------------------------------------------------

总结:以上提供的几种方案都可以解决跨域问题,视自己业务的实际情况选择方案;个人选择的是最简单的Nginx,不对代码进行任何的修改,直接配置在代理中;个人觉得要达到比较细粒度的各种优化,推荐使用第五种方案,但同时会会增加学习成本,对时间比较充足的同学可以研究研究;

  虽然跨域调用的问题解决了,但是后来发现以上方法并不能解决cookies的跨域问题,导致在A域名下点击登录后直接跳转到B域名下已登陆后的一个页面,会失败。主要问题是在A域名下获取到的cookie不能共享到B域名下,导致跳转B域名下已登录的页面会被判定没有coolie(没有登录),会导致直接跳转到B域名下的登录页面进行重新登录,即在A域名下的登录失败了;思考了几种解决方案:①后端修改登录接口的代码逻辑,适配出现的这种情况(创建新的cookie,或者由后端进行路由),②在A域名下点击登录时,直接跳转到B域名下的一个和A域名页面一样的登录页面,不给用户感知,但域名会发生变化,在B域名下的登录页面进行实际的登录操作,就不会再产生cookie跨域的问题了,③直接使用nginx进行路由:

以上三种解决思路可供参考,我自己选用的第二种,如果后端和运维支持比较给力,推荐选用第一种或者第三种解决方案!!!!

参考Blog:1、https://www.cnblogs.com/Coding-net/p/6207122.html

             2、http://www.cnblogs.com/sloong/p/cors.html

记一次springMVC的跨域解决方案的更多相关文章

  1. SpringMVC解决跨域问题

    有个朋友在写扇贝插件的时候遇到了跨域问题. 于是我对解决跨域问题的方式进行了一番探讨. 问题 API:查询单词 URL: https://api.shanbay.com/bdc/search/?wor ...

  2. [转载]SpringMVC解决跨域问题

    本文转载自  https://www.cnblogs.com/morethink/p/6525216.html SpringMVC解决跨域问题, 感谢作者! 有个朋友在写扇贝插件的时候遇到了跨域问题. ...

  3. java springmvc 前端 跨域问题

    有个朋友在写扇贝插件的时候遇到了跨域问题.于是我对解决跨域问题的方式进行了一番探讨. 问题 API:查询单词URL: https://api.shanbay.com/bdc/search/?word= ...

  4. 跨域解决方案一:使用CORS实现跨域

    跨站HTTP请求(Cross-site HTTP request)是指发起请求的资源所在域不同于请求指向的资源所在域的HTTP请求. 比如说,我在Web网站A(www.a.com)中通过<img ...

  5. asp.net web api2.0 ajax跨域解决方案

    asp.net web api2.0 ajax跨域解决方案 Web Api的优缺点就不说了,直接说怎么跨域,我搜了一下,主要是有两种.  一,ASP.NET Web API支持JSONP,分两种 1, ...

  6. SpringMvc支持跨域访问,Spring跨域访问,SpringMvc @CrossOrigin 跨域

    SpringMvc支持跨域访问,Spring跨域访问,SpringMvc @CrossOrigin 跨域 >>>>>>>>>>>> ...

  7. iframe跨域解决方案

    公司某个功能用的是iframe,由于跨域的原因,我们不能直接设置父级页面iframe的高度,所以用了一个中间页home来完成父级页面iframe的高度设置,这种中间页其实很多时候不好用,因为涉及到页面 ...

  8. SpringMvc支持跨域访问,Spring跨域访问,SpringMvc @CrossOrigin 跨域[转]

    SpringMvc支持跨域访问,Spring跨域访问,SpringMvc @CrossOrigin 跨域 原文地址:https://www.cnblogs.com/fanshuyao/p/716847 ...

  9. JSON跨域解决方案收集

    最近面试问的挺多的一个问题,就是JavaScript的跨域问题.在这里,对跨域的一些方法做个总结.由于浏览器的同源策略,不同域名.不同端口.不同协议都会构成跨域:但在实际的业务中,很多场景需要进行跨域 ...

随机推荐

  1. LN : leetcode 123 Best Time to Buy and Sell Stock III

    lc 123 Best Time to Buy and Sell Stock III 123 Best Time to Buy and Sell Stock III Say you have an a ...

  2. Oracle Mysql的jdbc连接

    Oracle和MySql的jdbc或连接池中的连接,写下来以便随时参考 Oracle: driverClassName=oracle.jdbc.driver.OracleDriver url=jdbc ...

  3. jvm 脑图

  4. java格式化sql

    在日志分析中,经常会对记录的sql进行分析,所以将一整行sql格式化,进行多行缩就显得很有必要,许多数据库客户端都提供sql的格式化功能,但复杂的多层嵌套sql往往格式化的l还不够友好,所以就自己造了 ...

  5. nginx php 配置模板

    server {     listen 80;     server_name    www.xxx.com;     #access_log     logs/www.xxx.com.access. ...

  6. 关于SQL Server数据表的五种约束

    1.主键约束(PRIMARY KEY) 主键约束可以在表中定义一个主键值,它可以唯一确定表中每一条记录,每个表中只能有一个主键约束(只能有一个主键约束的意思并不是说受主键约束的列只能有一个),并且受主 ...

  7. 安卓 Android 简单数据库(增删改查)

    <Button android:id="@+id/delete_btn" android:layout_width="wrap_content" andr ...

  8. close - 关闭一个文件描述符

    SYNOPSIS 总览 #include <unistd.h> int close(int fd); DESCRIPTION 描述 close 关闭 一个 文件 描述符 , 使它 不在 指 ...

  9. OpenCV2:应用篇 QT+OpenCV实现图片编辑器

    一.简介 做完会放在Github上  

  10. 正确地使用Context

    Context应该是每个入门Android开发的程序员第一个接触到的概念,它代表当前的上下文环境,可以用来实现很多功能的调用,语句如下. //获取资源管理器对象,进而可以访问到例如 string, c ...