前言
前端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))//原地址校验
{
//这里可以直接返回,无需继续往下执行
}
结语
以上是我在工作中对发送请求的一些认识,不足之后还请各位朋友指出,有不懂的地方或者更好的建议的可以给我留言,让我们一起共同进步。