服务跨域以及Session保持问题
前言随着前后端分离以及微服务的应用越来越广,跨域与session保持问题已经是大家所熟知的问题,由于项目需要本周进行了相应资料的调研和测试,最终在客户端,前台,代理,服务端等多个层面上共同解决了这个问题,特别在此记录一下,希望能帮助大家。跨域来自于浏览器的安全基石,即”同源政策”(same-origin policy)。
前言
随着前后端分离以及微服务的应用越来越广,跨域与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的改造,虽然各方面技术都已经非常成熟,但涉及到细节众多,要彻底解决还是要消耗相当的精力。
但还有一个非常重要的问题那就是跨域产生的安全问题,需要大家额外注意,毕竟当安全有问题时,一切其他的问题就都不是问题了。
更多推荐
所有评论(0)