前言
前端js發送ajax請求來獲取數據是很常見的一種方式,通過這段時間的工作,我對這個發送請求的js進行了封裝,使用起來更加方便,還有可能涉及到的一些安全知識,接下來我就給各位朋友分享一下。
正文
1、ajax請求的封裝
下面這個是我對發送ajax請求進行了封裝,好用又實在。
var remindAjax={
jsonpCall:function(url,param,beforesendfn,successfn,errorfn){
$.ajax({
type: "POST",
async: true,
dataType: "jsonp",
jsonp: "jsonpCallBack",
url: url,
data: param,
jsonpCallback:"callBackHandler" ,
beforeSend: function(){
beforesendfn();
},
success: function (data) {
successfn(data);
},
error:function(data){
errorfn(data);
}
});
},
ajaxPost:function(url,param,beforesendfn,successfn,errorfn){
$.ajax({
type: "POST",
async: true,
dataType: "json",
url: url,
data: param,
beforeSend: function(){
beforesendfn();
},
success: function (data) {
successfn(data);
},
error:function(data){
errorfn(data);
}
});
}
}
以前我發送異步請求的時候,一般使用下面這種寫法,但是後來在工作中遇到這個封裝的函數,將發請求部分提取了出來,減少了冗餘的代碼。
$.ajax({
type: "POST",
async: true,
dataType: "json",
url: url,
data: param,
success: function (data) {
successfn(data);
}
});
如果你想調用這個函數的時候,可以參照下面的寫法:
var param = new Object();
param.operate=operate;
remindAjax.ajaxPost(basedata.Uri.GetAjaxHandler() +"/insertUserClick.html",param,
function(){
console.log(operate);
},
function (data) {
if (data == null) return;
},
function(data)
{
alert("服務器錯誤,請嘗試");
}
);
其實,上面的代碼。你還可以繼續封裝成一個函數,不過就是參數和請求地址,結果處理都固定了下來,如果使用頻率高的話可以考慮
function insertLog(operate){
var param = new Object();
param.operate=operate;
remindAjax.ajaxPost(basedata.Uri.GetAjaxHandler() +"/insertUserClick.html",param,
function(){
console.log(operate);
},
function (data) {
if (data == null) return;
},
function(data)
{
alert("服務器錯誤,請嘗試");
}
}
調用的時候傳入參數即可,比如:insertLog(‘fristLoad’);
請求地址url部分還可以在繼續寫成函數basedata.Uri.GetAjaxHandler()
var basedata={
Uri: {
GetAjaxHandler: function() {
return window.location.href.split('?')[0].substring(0, window.location.href.split('?')[0].lastIndexOf('/'));
}
},
getQueryString: function()
{
var reg = new RegExp("(^|&)SP=([^&]*)(&|$)");
var r = window.location.search.substr(1).match(reg);
if(r!=null)return unescape(r[2]); return '';
},
closeTab:function(){
var userAgent = navigator.userAgent;
if (userAgent.indexOf("Firefox") != -1 || userAgent.indexOf("Chrome") != -1) {
window.location.href = "about:blank";
window.top.opener = null;
window.close();
} else {
window.opener = null;
window.open("", "_self");
window.close();
}
}
}
Uri 這個函數是用來獲取當前請求地址的,也就是說,如果地址的前綴都一樣,那麼使用
basedata.Uri.GetAjaxHandler() +"/insertUserClick.html
這個寫法就可以鏈接到你想到的地址,而不用每次都寫絕對地址,例如:http://192.168.168.213:8008/hyhb_pt/remind/hy0222/ + 你的controller地址,比如insert.do 拓展性會好一點。getQueryString是用來截取參數的,這裏是截取‘’sp=‘’後面的參數。closeTab 這個是用來關閉當前瀏覽器頁面的,不過的瀏覽器效果也許不大一樣。
2、後臺接收參數
比如說,如果你是下面的這種寫法
var param = new Object();
param.operate=operate;
那麼接收參數的時候可以寫成這樣
String operate = (String)request.getParameter("operate")
3、發送請求前的操作
如果是點擊事件,通過簡單的js校驗,然後發送請求,那麼在點擊之後,服務端返回數據之前,應該將這個按鈕鎖定起來,讓它不可點擊。我常遇到的有兩種,第一種就是input 標籤 ,如果你是這種寫法的
function click() {
$('.btn').on('click', function() {
//校驗通過 解綁按鈕
$('.btn').unbind('click');
........
//請求完成,繼續綁定。
// click();
});
}
第二種就是圖片標籤,比如說下面代碼,一般是通過給標籤一個id,然後綁定點擊事件,這個時候,我的建議是加遮罩層,也就是點擊之後給圖片加上遮罩層,讓她無法點擊,請求完成之後去掉遮罩層。
<img class="btn" src="../resources/style/time/images/rightNow.png" alt="" id="push_click">
通過以上兩種方式的使用,可以避免由於網絡延遲導致用戶在返回前多次點擊按鈕,發送多次請求,消耗服務器的資源,有時候會導致一些重複的數據(單身多年,手速夠快是可以實現在返回前多次點擊的)。所以說,鎖定按鈕的方式還是很有必要的。
4、涉及到的服務端安全問題,源地址校驗
其實這裏涉及到一個安全問題,就是java代碼也可以實現發出post和get的請求。一般來說我們默認從客戶端發送的請求是正常請求,但是如果這個數據包被別人攔截,解析你的url和參數,然後用其它語言(比如java)來給這個請求地址發送他自己捏造的參數(參數名字和順序一樣,參數值不一樣),也可以正常的請求到我們的數據,這就存在一定的安全隱患。
所以我的建議是在java代碼中添加源地址校驗,爲什麼可以這麼做呢?從客戶端發出的ajax 請求和自己寫java代碼實現的請求,有一個區別就是請求頭的不一樣,比如說你部署應用的服務器ip是192.168.5.125,那麼客戶端發送請求到你的服務器,帶過來的地址就是192.168.5.125;不過你要是部署在本地(比如你的ip爲192.1.1.14),比如說部署到自己的tomcat服務器,那麼用代碼實現發請求,那麼帶過去的請求頭就會出現192.1.1.14這個地址,這就是客戶端發請求和自己寫java代碼發請求很明顯的一個區別。所以我們可以通過這個區別來做文章。
請看下面這個工具類的代碼:
public class Tools {
public static boolean ValidReferer(HttpServletRequest request) {
String referer = request.getHeader("referer");
Log.debug("referer="+referer);
if (referer == null)
return false;
List<String> host = ReadAllowIPXml();
if (host != null && host.size() > 0) {
for (int i = 0; i < host.size(); i++) {
if (referer.startsWith("http://" + host.get(i)))
return true;
}
}
return false;
}
private static List<String> ReadAllowIPXml() {
List<String> strs = null;
try {
File f = new File(Tools.class.getClassLoader().getResource("conf/allowIP.xml").getPath());
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
Document doc = builder.parse(f);
NodeList nl = doc.getElementsByTagName("ip");
if (nl != null && nl.getLength() > 0)
strs = new ArrayList<String>();
for (int i = 0; i < nl.getLength(); i++) {
strs.add(nl.item(i).getFirstChild().getNodeValue());
}
} catch (Exception e) {
e.printStackTrace();
}
return strs;
}
}
每次控制器接收到ajax請求的時候,就去讀取allowIP.xml配置文件,然後看看是否存在列表中,如果在的話,就視爲正常請求。
這個是allowIP.xml裏面的寫法,裏面就是你允許訪問你得控制器的服務器地址,,我在實踐的時候這裏覺得這裏應該有兩種寫法,主要是看你的請求地址,如果是http://192.168.4.242:8080/edu/remindpage.do這種,那麼你應該配置ip地址,如果是https://www.baidu.com/這種,你就可以配置域名,其實這個還是受限於前面的工具類代碼,如果你有興趣的話,可以改寫成更好的代碼(這樣配置文件只需要寫ip地址或者域名即可)。
<?xml version="1.0" encoding="UTF-8"?>
<ips>
<ip>192.168.4.242</ip>
<ip>www.baidu.com</ip>
</ips>
請求進入controller控制器,通過下面的代碼
if(!Tools.ValidReferer(request))//原地址校驗
{
//這裏可以直接返回,無需繼續往下執行
}
結語
以上是我在工作中對發送請求的一些認識,不足之後還請各位朋友指出,有不懂的地方或者更好的建議的可以給我留言,讓我們一起共同進步。