在開發中總是會遇到,接入層可能去調用其它域名下服務的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?