服务调用跨域以及Session保持问题

简介:

前言

随着前后端分离以及微服务的应用越来越广,跨域与session保持问题已经是大家所熟知的问题,由于项目需要本周进行了相应资料的调研和测试,最终在客户端,前台,代理,服务端等多个层面上共同解决了这个问题,特别在此记录一下,希望能帮助大家。

为什么跨域

跨域来自于浏览器的安全基石,即”同源政策”(same-origin policy)。在浏览器看来,不同的服务端的域名或不同的端口不是同源的,即都是不受信任的,即要求协议,域名,端口全部相同。

具体如下所示。

http://www.example.com/dir2/other.html:同源
http://example.com/dir/other.html:不同源(域名不同)
http://v2.www.example.com/dir/other.html:不同源(域名不同)
http://www.example.com:81/dir/other.html:不同源(端口不同)

而在实际项目或产品中,基本不可能与前台交互的都是一个源,或多或少的都会获取其他域的服务,因此就产生了常见的跨域问题。

怎么跨域

常见的跨域方法如下所示。

代理

采用Nginx的反向代理将前台的请求转发到后台,将前台的请求转变为应用程序到服务端的请求,曲线救国。

要达到这个目的需要部署nginx服务器和进行适当的配置,具体配置可参考如下博客

JSONP

JSONP是服务器与客户端跨源通信的常用方法。最大特点就是简单适用,老式浏览器全部支持,服务器改造非常小。

它的基本思想是,网页通过添加一个

$.ajax({
   async:false,
   url: http://跨域的dns/document!searchJSONResult.action,
   type: "GET",
   dataType: 'jsonp',
   jsonp: 'jsoncallback',
   data: qsData,
   timeout: 5000,
   beforeSend: function(){
   //jsonp 方式此方法不被触发.原因可能是dataType如果指定为jsonp的话,就已经不是ajax事件了
   },
   success: function (json) {//客户端jquery预先定义好的callback函数,成功获取跨域服务器上的json数据后,会动态执行这个callback函数
    if(json.actionErrors.length!=0){
           alert(json.actionErrors);
     }
       genDynamicContent(qsData,type,json);
   },
    complete: function(XMLHttpRequest, textStatus){
    $.unblockUI({ fadeOut: 10 }); 
   },
   error: function(xhr){
    //jsonp 方式此方法不被触发.原因可能是dataType如果指定为jsonp的话,就已经不是ajax事件了
    //请求出错处理
    alert("请求出错(请检查相关度网络状况.)");
   }
});

或更简洁一点:

$.getJSON(" http://跨域的dns/document!searchJSONResult.action?name1="+value1+"&jsoncallback=?",
      function(json){
      if(json.属性名==值){
      // 执行代码
            }
        });

JSONP只能执行GET,其他的方法无法执行。

CORS

CORS是一个W3C标准,全称是”跨域资源共享”(Cross-origin resource sharing)。

它允许浏览器向跨源服务器,发出XMLHttpRequest请求,从而克服了AJAX只能同源使用的限制。参考

CORS还是存在一些限制,如下图所示

原始的服务端需要比较大的改造。但目前大部分框架已经集成了相应的插件,使CORS的应用不再像以前那么困难。

比如SrpingBoot,只需要进行简单的改造即可

@Configuration
public class MyConfiguration {
@Bean
public WebMvcConfigurer corsConfigurer() {
    return new WebMvcConfigurerAdapter() {
        @Override
        public void addCorsMappings(CorsRegistry registry) {
            registry.addMapping("/\*\*");
        }
    };
}
}

具体方法请移步。

webSocket

WebSocket是一种通信协议,使用ws://(非加密)和wss://(加密)作为协议前缀。该协议不实行同源政策,只要服务器支持,就可以通过它进行跨源通信。

当然这个方法在跨域中并不常见,这里只是简单的提一下。

当跨域遇到了Session

Session的含义

Session:在计算机中,尤其是在网络应用中,称为“会话控制”。Session 对象存储特定用户会话所需的属性及配置信息。即服务端用于区分特定用户的数据结构。一般的做法都是在客户端通过验证后,服务端将一个sessionId告诉客户端,在随后的通讯中,客户端将sessionId告诉服务端,服务端通过验证sessionId的方法来确认客户端的身份。这里面的核心就是session是在服务端管理的,将id交给客户端用作区分。

因此在跨域时,需要在各个环节上都要保证客户端的sessionid不能丢失,否则就会被服务端拦截,无法获取服务的问题。

前台的Session保持

前台一般通过各种前后端交互框架实现从后台获取数据的,比如fetch,ajax,http等。下面就从这三个为例子简单介绍一下怎么保持session问题(实际上是携带cookie)。

Fetch

设置 credentials: “include”

getData: function (v,  callback, errorCallBack) {
let url = URL + '/list?version='+v;
fetch(url, {
    method: 'GET',
    credentials: "include"
}).then((response)=>response.json())
    .then((responseJsonData)=> {
        callback && callback(responseJsonData);
    }).catch((error)=> {     
    });
},

jquery

一般需要采用全局设置的方式

$.ajaxSetup({xhrFiled:{
'withCredentials':true
}})
$.ajax(url:url,method:"get",success:function(data){console.log(data)});

Angular

设置{‘withCredentials’:true}

function get($scope,$http){
 $http.post(url,
 {v1:'name_eu'},{'withCredentials':true})
 .success(function(data){ $scope.var= data; }); 
 }

客户端的session保持

这里的客户端指的是与服务端通讯的java,C#等单独的应用程序,实际上客户端本身是不存在纯粹的跨域问题,因为没有浏览器的同源策略的限制。但仍然存在session保持问题,同时由于不是前台,没有浏览器帮助解决cookie的携带问题,因此客户端的session保持问题需要对验证过程的更深入的理解才能完美解决

在上文中我们已经谈到了实际上客户端(无论是前台还是应用程序)都是拿着sessionId向服务端进行验证的。我们通过浏览器开发工具可以看到,前端的sessionId是服务端写入到Cookie中的,而且一般是httpOnly属性(这也意味着我们不能通过普通的方法从cookie中将这个sessionId取出来),在向服务端发送请求时浏览器自动携带的,前台完成这一切只需要设置 credentials字段,其余的全部由浏览器或框架自动完成。但在客户端这一过程需要我们手动完成。下面我们就简单介绍以下,这一过程的基本原理。

1.向服务端发发送登录请求
2.在获取到服务端的反馈结果后,检查是否登录成功
3.若登录成功则获取该请求的cookie数据(JSESSIONID),并记录在缓存中。
4.按照业务需求发送其他请求,但要在每次发送时在request的header中添加Cookie,JESSIONID=缓存值(实际工程里我们可以检测每个cookie找到name=jsessionid的项,把整个项缓存起来)

值得注意的是,很多服务端都是有session过期的问题,因此需要更一段时间登录一次以保证session没有过期。

代理服务的设置

这里的代理服务器主要是nginx

在设置nginx要注意需要设置cookie的转发,否则这个session是保持不了的

在服务器设置部分

proxy_set_header Cookie $http_cookie

服务端的设置

最重要的当然是服务端的设置,否则一切都是白扯

header('Access-Control-Allow-Origin:http://127.0.0.1:80');  
header("Access-Control-Allow-Methods:HEAD,POST,GET,PUT,DELETE,OPTIONS"); 
header('Access-Control-Allow-Credentials: true');

注意由于设置了Access-Control-Allow-Credentials: true,因此必须设置Origin不能使用通配符(*)。

总结

跨域与Session保持问题涉及到系统的方方面面,从前台配置携带Cookie,客户端手动缓存和携带sessionId,反向代理服务器的设置到服务端CORS的配置,以及可能的JSONP的改造,虽然各方面技术都已经非常成熟,但涉及到细节众多,要彻底解决还是要消耗相当的精力。

但还有一个非常重要的问题那就是跨域产生的安全问题,需要大家额外注意,毕竟当安全有问题时,一切其他的问题就都不是问题了。

往期文章

Nginx系列教程(1)nginx基本介绍和安装入门

Nginx系列教程(2)nginx搭建静态资源web服务器

Nginx系列教程(3)nginx缓存服务器上的静态文件

Nginx系列教程(4)nginx处理web应用负载均衡问题以保证高并发

Nginx系列教程(5)如何保障nginx的高可用性(keepalived)

Nginx系列教程(6)nginx location 匹配规则详细解说

Nginx系列教程(7)nginx rewrite配置规则详细说明

Nginx系列教程(8)nginx配置安全证书SSL

Nginx系列教程(9)nginx 解决session一致性

相关实践学习
基于函数计算快速搭建Hexo博客系统
本场景介绍如何使用阿里云函数计算服务命令行工具快速搭建一个Hexo博客。
相关文章
|
8月前
|
小程序
小程序接口请求配置
小程序接口请求配置
149 0
|
8月前
|
JSON 缓存 安全
跨域时怎么处理 cookie?
跨域时怎么处理 cookie?
|
12月前
|
数据采集 负载均衡 监控
三种跨域解决方案:HttpClient、注解、网关
三种跨域解决方案:HttpClient、注解、网关
|
安全 JavaScript 前端开发
什么是跨域,如何解决跨域?
还在等什么,快来一起讨论关注吧,公众号【八点半技术站】,欢迎加入社群
什么是跨域,如何解决跨域?
|
前端开发 API
axios定制化设置请求响应拦截器,统一处理请求响应
设置拦截器的目的在于:可以定制化,设置请求头,公共api,超时时间。统一处理响应,对于前端获取的数据更加清晰。
368 0
|
Web App开发 JavaScript 前端开发
【跨域】一篇文章彻底解决跨域设置cookie问题!
之前做项目的时候发现后端传过来的 SetCookie 不能正常在浏览器中使用。是因为谷歌浏览器新版本Chrome 80将Cookie的SameSite属性默认值由None变为Lax。接下来带大家解决该问题。
2526 0
|
JSON JavaScript 前端开发
跨域请求的基本实现几种方式
跨域请求的基本实现几种方式
跨域请求的基本实现几种方式
|
存储 JSON 缓存
|
安全 JavaScript 前端开发
什么是跨域?如何解决跨域?
解决好跨域,让我们愉快的开发吧
483 4
什么是跨域?如何解决跨域?
|
存储 NoSQL 前端开发
Koa 鉴权实战 - Session/Cookie
Koa 鉴权实战 - Session/Cookie