crosDomain.png
服务器端解决,通过一个filter过滤来自移动端服务器的请求,为response添加Access-Control-Allow信息。通过设置Access-Control-Allow-Credentials=true允许客户端发送cookie,客户端需要设置XMLHttpRequest()的withCredentials 属性为true
var xhr = new XMLHttpRequest();
xhr.withCredentials = true;
在ionic中需要设置$http:
$http({
method: "GET",
url: url,
params: params,
cache: false,
timeout: 20000,
withCredentials:true,
headers: {'Content-Type': 'application/json;charset=UTF-8','Cache-Control':'no-cache'}
}).then(function (result) {
...
}
跨域小知识
客户端ClientA请求服务器server1,server1的页面中使用ajax请求了server2中的数据就发生了跨域。默认情况,浏览器不允许跨域请求。大致解决思路有:
浏览器端解决。
适合开发者自己调试, 在chrome浏览器中添加启动参数:
--args --disable-web-security --user-data-dir
server端解决方案JSONP。
通过某些手段规避浏览器的跨域规则,比如使用JSONP等,JSONP需要客户端与服务器端配合。
网页通过动态插入<script>元素,由它向跨源网址发出请求,不再受跨域规则限制。
推荐JQuery,在ajax请求中指定dataType:'jsonp'的方式。这种方式自动实现<script>加回调函数的效果。
js代码
<script type="text/javascript" src="jquery.js"></script>
<script type="text/javascript">
$.ajax({
url:"http://crossdomain.com/services.php",
dataType:'jsonp',
data:'',
jsonp:'callback',
success:function(result) {
for(var i in result) {
alert(i+":"+result[i]);//循环输出a:1,b:2,etc.
}
},
timeout:3000
});
</script>
这种方法需要js实现处理返回结果的回调函数,如果有很多页面跨域,实现这些回调函数挺烦人的。
服务器端实现,在原来的返回结果基础上添加回调函数名称和括号,默认用success,也可以配合jQuery的ajax请求指定函数名称。
支持跨域前的返回:
['data1','data2']
为支持jsonp,修改后的返回:
callbackFunction(['data1','data2'])
server端实现方案2,通过Allow-Control-Access机制。
现代浏览器都通过Allow-Control-Access机制允许跨域,原理如下:
- 浏览器发现存在跨域请求,不直接拒绝,而是向服务器server2发多送一个Origin字段把server1的IP地址加端口告知server2,如果是put、delete这种高危请求,浏览器先发送一个options类型的请求。
- 如果server2没有任何允许跨域的意思,浏览器不再处理服务器的响应,而是报一个跨域错误。
- 如果server2允许跨域,需要在response中通知浏览器,告知浏览器允许谁跨域,允许不允许浏览器发送cookie信息。
- 浏览器检查server2返回的信息,如果当前请求合乎规则,则进一步处理服务器端响应,执行js,展示页面。
server2允许跨域的java端实现,通过filter为response添加Allow-Control-Access信息:
package org.example.core.filter;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import jodd.util.StringUtil;
/**
* @author damowang 20170802 解决移动端开发浏览器跨域问题
*
*/
public class CORSFilter implements Filter {
/**客户端域名,通过init-param配置,在filter初始化时赋值*/
private String allowOrigin;
@Override
public void destroy() {
// TODO Auto-generated method stub
}
@Override
public void doFilter(ServletRequest srequest, ServletResponse sresponse,
FilterChain chain) throws IOException, ServletException {
HttpServletResponse response = (HttpServletResponse) sresponse;
HttpServletRequest request = (HttpServletRequest) srequest;
//设置允许谁(server1的域名或ip)跨域,在一次request中Access-Control-Allow-Methods只能设置一个客户端地址,因此通过判断客户端设置该字段
if (StringUtil.isNotEmpty(allowOrigin)) {
List<String> allowOriginList = Arrays.asList(allowOrigin.split(","));
String currentOrigin = request.getHeader("Origin");
if (allowOriginList.contains(currentOrigin)) {
response.setHeader("Access-Control-Allow-Origin", currentOrigin);
}
}
response.setHeader("Access-Control-Allow-Methods", "POST,GET,OPTIONS");
response.setHeader("Access-Control-Allow-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Headers", "x-requested-with,Authorization,Cache-Control,Content-Type");
response.setHeader("Access-Control-Allow-Credentials", "true");
chain.doFilter(request, response);
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {
allowOrigin = filterConfig.getInitParameter("allowOrigin");
}
}
web.xml中配置拦截器
<!-- 跨域拦截器 -->
<filter>
<filter-name>corsFilter</filter-name>
<filter-class>org.example.core.filter.CORSFilter</filter-class>
<init-param>
<param-name>allowOrigin</param-name>
<param-value>http://server1.com:8100,http://anotherDomainOfServer1.com</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>corsFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
客户端允许发送cookie:
var xhr = new XMLHttpRequest();
xhr.withCredentials = true;