瀏覽器存在許多安全策略,其中同源策略就是其中一個,所謂同源策略也叫同域名策略,即只有協議+域名+端口一致的情況下才可以相互訪問,其目的就是為了保護用戶信息的安全,同源策略現在的范圍包括三方面:1)、Cookie、LocalStorage、IndexDB無法讀取;2)、DOM無法獲取;3)、AJAX請求不能發送。這里主要介紹兩種解決AJAX請求不能發送的解決方案:JSONP和CORS。JSONP是一種前端的解決方式;CORS是跨域資源共享,在服務端實現。
一、JSONP
這里使用jquery的$.ajax()方法來實現JSOP跨域訪問,其代碼如下,
$(document).ready(function() { $('#btn').on('click', function() { $.ajax('http://localhost:8081/springmvc/my/my2/1/1', { dataType : 'jsonp', type : 'get', jsonpCallback: "aa", success : function(data,status) { console.log('111:'+data.Hello+' '+data); } }) }) })
這里使用jquery通過給一個按鈕注冊一個點擊事件來觸發跨域操作,具體看ajax()函數中的參數,第一個指定了一個URL(restful的形式),然后是必須指定dataType為jsonp,指定請求的類型為get,接着jsonCallBack參數指定為aa,這里這個參數的鍵及鍵值都可以隨便指定,然后是成功之后的回調函數,實現了控制台打印。
下面是后台代碼:
@RequestMapping("my2/{courseId}/{name}") @ResponseBody public String method2(@PathVariable("courseId") String courseId,@PathVariable("name") String name){ //必須是這種形式的字符串,其中小括號內的字符串必須是標准的json格式的
String str="aa({\"Hello\":\"World\"})";
return str; }
這里簡單的返回了一個字符串,可以看到字符串的形式比較特別,會發現很像JavaScript中的函數調用(aa({"Hello":"World"})),函數aa的形參是一個對象,恭喜你,這里確實是函數的調用,且函數名稱必須和前邊的jsonCallBack參數定義的值一致,在前端請求的時候的參數jsonCallBack的值即是這里的函數名。
其實jsonp的方式就是會動態的執行js代碼,從而實現跨域。
這種方式必須是get方式的請求,且在后台需要動態獲得一個參數的值,即jsonCallBack的值,獲得此參數的值之后和要返回的字符串進行拼接;如果要傳的參數比較多,這種方式就可以了。
二、CORS
CORS是跨域資源共享,通過設置響應頭的Access-Control-Allow-Origin屬性,從而控制跨域資源訪問,通常Access-Control-Allow-Origin的值可以設為“*”,代表對所有的域都可以實現跨域,也可以單獨設置為某個域,如http://10.10.18.98:9090,則表示這個域可以跨域訪問。
由於需要給響應設置Access-Control-Allow-Origin屬性,這里使用filter,代碼如下
package com.cn.my.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; public class CORSFilter implements Filter { @Override public void destroy() { // TODO Auto-generated method stub } //實現向響應頭部添加Access-Control-Allow-Origin屬性,實現跨域訪問。 @Override public void doFilter(ServletRequest sRequest, ServletResponse sResponse, FilterChain chain) throws IOException, ServletException { // TODO Auto-generated method stub HttpServletRequest request=(HttpServletRequest)sRequest; HttpServletResponse response=(HttpServletResponse)sResponse; response.setHeader("Access-Control-Allow-Origin", "*"); chain.doFilter(sRequest, sResponse); } @Override public void init(FilterConfig arg0) throws ServletException { // TODO Auto-generated method stub } }
以上是一個filter,可以對所有的域進行跨域訪問本域。下面是filter的配置,
<!--跨域訪問--> <filter> <filter-name>CORS</filter-name> <filter-class>com.cn.my.filter.CORSFilter</filter-class> </filter> <filter-mapping> <filter-name>CORS</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
以上是在web.xml中進行的filter的配置,配置了服務端的跨域訪問,在前端可以使用普通的ajax進行訪問,
$('#btn').on('click', function() { $.ajax('http://localhost:8081/springmvc/my/my2/1/1', { type : 'get', success : function(data,stats) { console.log('111:'+data.sex+' '+data); } }) })
上面的ajax請求是一般的請求,,同樣可以獲得后台的值,且后台代碼不需要返回特定的字符串,后台只需要返回JSON即可。如下,
@RequestMapping("my2/{courseId}/{name}") @ResponseBody public Map<String,String> method2(@PathVariable("courseId") String courseId,@PathVariable("name") String name){ HashMap<String,String> map=new HashMap<String,String>(); map.put("name", "劉三"); map.put("sex", "女"); map.put("age", "47"); return map; }
上面是返回json的后台方法,沒有任何的改變。
使用CORS的方式在IE10之下的瀏覽器是不支持的,下面是一張從網上download的圖,可以看到CORS的瀏覽器的支持情況,
通過兩種不同方式的比較可以看出,JSONP對老瀏覽器支持比較好,但使用起來稍微麻煩,CORS的方式配置簡單,只需要一個Filter即可,但是需要較新的瀏覽器,值得慶幸的是,現在大多數瀏覽器都支持了CORS。
有不正之處歡迎指正!謝謝!