在开发中总是会遇到,接入层可能去调用其它域名下服务的api,crud数据,可是在这中间会出现js的同源策略,导致同一个DOM不能用多个源加载数据,已确保安全性。
在数据远程调用的设计时候,要考虑性能又要考虑安全性,下边为总结的跨域三种实现:
1:使用ACAO(‘Access-Control-Allow-Origin’)设置响应头域名访问-->问题低版本的ie(10及以下)和个别浏览器并不支持。 2:使用jsonp实现跨域数据请求,可以但是,所有的请求会过滤成GET请求,如果安全数据的读写,有不安全因素。 3:使用form表单提交+target属性指向iframe中代码解析,可以处理,“稍微”有点麻烦。测试添加配置本地的host:C:\Windows\System32\drivers\etc\HOSTS
10.1**.11.76 www.baidux.com 10.1**.11.76 www.alibabax.com
1:ACAO
服务器(spring-mvc):
①:配置web.xml,过滤所有的*.json或自定义其他结尾的请求:
<filter> <description>js跨域</description> <filter-name>JsCrossDomainFilter</filter-name> <filter-class>cn.***.filter.JsCrossDomainFilter</filter-class> </filter> <filter-mapping> <filter-name>JsCrossDomainFilter</filter-name> <url-pattern>*.json</url-pattern> </filter-mapping>②:cn.***.filter.JsCrossDomainFilter实现代码:
package cn.***.filter; import java.io.IOException; 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 org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.module.util.NetworkUtil; /** * js跨域设置过滤器 * @author Tony_tian * 过滤:1:请求浏览器类型记录,js跨域设置 * 2:为了不多余设置其他请求,此过滤器只过滤:*.json结尾的请求 * 注:浏览器兼容注解: * a:大概在iex~10浏览器: * ①:Ajax的error打印出来提示no transport:解决:js中第一行加入即可:jQuery.support.cors = true; * ②:error没有权限:IE浏览器的安全性设置问题: * 解决:点击IE浏览器的的“工具->Internet 选项->安全->自定义级别”将“其他”选项中的“通过域访问数据源”选中为“启用”或者“提示”,点击确定就可以了。 * */ public class JsCrossDomainFilter implements Filter{ //日志 private static final Logger LOG = LoggerFactory.getLogger(JsCrossDomainFilter.class); @Override public void destroy() { } @Override public void doFilter(ServletRequest req, ServletResponse res,FilterChain chain) throws IOException, ServletException { HttpServletRequest request = (HttpServletRequest) req; HttpServletResponse response = (HttpServletResponse) res; String type = NetworkUtil.getBrowser(request); NetworkUtil.setJsCrossDomain(type, response); LOG.info("请求浏览器类型:" + type + "---> js跨域已设置"); chain.doFilter(request, response); } @Override public void init(FilterConfig config) throws ServletException { } }③:NetworkUtil实现代码:
package com.module.util; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.util.StringUtils; /** * 获取请求地址, ip地址 * @author Administrator * */ public class NetworkUtil { public static String getCurrentURL( HttpServletRequest request ) { StringBuffer url = new StringBuffer( ); String appName = request.getContextPath( ); if ( appName != null && appName.trim( ).length( ) > 0 && !appName.trim( ).equals( "/" ) ) url.append( appName ); String page = request.getServletPath( ); if ( page != null ) url.append( page ); String queryString = request.getQueryString( ); if ( queryString != null ) url.append( "?" + queryString ); return url.toString( ); } public static String getIpAddr( HttpServletRequest request ) { String ip = request.getHeader("X-Forwarded-For"); if(!StringUtils.isEmpty(ip) && !"unKnown".equalsIgnoreCase(ip)){ //多次反向代理后会有多个ip值,第一个ip才是真实ip int index = ip.indexOf(","); if(index != -1){ return ip.substring(0,index); }else{ return ip; } } ip = request.getHeader("X-Real-IP"); if(!StringUtils.isEmpty(ip) && !"unKnown".equalsIgnoreCase(ip)){ return ip; } return request.getRemoteAddr(); } public static String getBrowser(HttpServletRequest request){ String userAgent = request.getHeader("user-agent"); if(userAgent.contains("MSIE")){ return "ie"; }else if(userAgent.contains("Firefox")){ return "firefox"; }else if(userAgent.contains("Chrome")){ return "chrome"; }else if(userAgent.contains("Safari")){ return "safari"; }else if(userAgent.contains("Opera")){ return "opera"; } return "other"; } /** * 服务器端设置js请求跨域。<br> * 注:javascript的同源策略:同源策略阻止从一个源加载的文档或脚本获取或设置另一个源加载的文档的属性。 * 如果它们的协议、端口(如果指明了的话)和主机名都相同。则他们属于同源。 * 简单说:浏览器限制脚本只能和同协议、同域名、同端口的脚本进行交互。<br> * 注: 由于IE8-IE10不支持通过设置Access-Control-Allow-Origin头的方式,所以对于IE需要按照IE提供的方案使用XDomainRequest对象解决。<br> * 备注:http://msdn.microsoft.com/en-us/library/ie/cc288060(v=vs.85).aspx <br> * 在使用此方法时在后端为了安全起见最好设置允许那些域进行跨域访问,如“shop.weibaobei.com",多个域名直接用“,”分开 * 注:addHeader()方法用指定的值添加 HTML标题。该方法常常向响应添加新的 HTTP标题。它并不替代现有的同名标题。一旦标题被添加,将不能删除。 * @author Tony_tian * @param type 如:request.getParameter("type"); request 代表客户端http请求信息对象 * @param response 代表服务器端http响应信息对象 * */ public static final void setJsCrossDomain(String type, HttpServletResponse response){ if("IE".equalsIgnoreCase(type)){ //对于IE需要按照IE提供的方案使用XDomainRequest对象解决 response.addHeader("XDomainRequestAllowed", "1"); }else{ //'*'表示允许所有域名访问,可以设置为指定域名访问,多个域名中间用','隔开 response.addHeader("Access-Control-Allow-Origin", "*"); } } }注:可配置更详细:
response.addHeader( "Access-Control-Allow-Origin", "*" ); response.addHeader( "Access-Control-Allow-Methods", "POST" ); response.addHeader( "Access-Control-Max-Age", "1000" );注:可以在ajax请求中多加入一个参数:
$.ajax({ url: "yoururl.json", type: "POST", crossDomain: true, //这个参数--未测试 data: data, dataType: "json", success:function(result){ alert(JSON.stringify(result)); }, error:function(xhr,status,error){ alert(status); } });
2:jsonp
ajax代码:function registerxx(){ var https_url_ts = "http://www.baidux.com:8080/timespacexstar/"; var timestamp = (new Date()).valueOf(); $.ajax({ url: https_url_ts + "registerjsonp.json?timestamp=" + timestamp, //请求url type: "post", //http请求方式 data:{"kaka" : "iamkaka"}, //序列化表单---data:$("#registerform").serialize(), async: true, //ajax请求是异步的 dataType: "jsonp", //返回数据类型jsonp格式 timeout: 60000, //请求超时时间 jsonp: "jsonpcallbackfunction",//服务端用于接收callback调用的function名的参数 jsonpCallback: "success_jsonpCallback",//callback的function名称,服务端会把名称和data一起传递回来 success:function(resdata){ var code = resdata.code; var message = resdata.message; var data = resdata.data; alert(code + "/" + message + "/" + data); } }); }注:页面请求的地址:
http://www.alibabax.com:8080/timespacexstar/register.html
服务器代码(spring-mvc):
import com.fasterxml.jackson.databind.util.JSONPObject;
@ResponseBody @RequestMapping(value = "registerjsonp", produces = "application/json; charset=UTF-8") public JSONPObject registerCT(Member registerParamsC, String jsonpcallbackfunction, HttpServletRequest request){ JsonResult json = new JsonResult(); String kaka = request.getParameter("kaka"); LOG.info("注册html信息数据记录111:" + jsonpcallbackfunction + " |~| " + kaka + " // " + request.getRequestURI() + "==" + request.getQueryString()); json.setCode(JsonResult.CODE_WARMTIPS); json.setData("success-ok" + kaka); json.setMessage("successful!!!"); JSONPObject jsonp = new JSONPObject(jsonpcallbackfunction, json); return jsonp; }
注:页面效果:
后台日志:
注册html信息数据记录111:success_jsonpCallback |~| iamkaka // /timespacexstar/registerjsonp.json==timestamp=1458791397407&jsonpcallbackfunction=success_jsonpCallback&kaka=iamkaka&_=1458791396313
3:form表单提交+target属性指向iframe中代码解析http://msdn.microsoft.com/en-us/library/ie/cc288060(v=vs.85).aspx
备注:
IE需要按照IE提供的方案使用XDomainRequest对象解决//////效果不佳
How to make a jsonp POST request that specifies contentType with jQuery?