关于具体的JavaScript安全机制,推荐请查看《JavaScript.The.Definitive.Guide.5th.Edition》。
网上解决跨域问题的文章不少,但都笼统的说了一下“因为JavaScript的安全框架”直接就跳过原因了,本着寻根问底的心态钻研了下。
关于JavaScript的安全性请记住一下几点,是简短的从《JavaScript.The.Definitive.Guide.5th.Edition》(我叫她"大犀牛")转述一下...注意不同浏览器也有不同限制:
1. JavaScript不能增删改查用户客户端的任何数据。
2. 无法打开socket或者接受其他主机请求。
注:如果没有了上面两条的话,网络变成什么世界就无法想象了。
3. 新建窗口事件只有在用户鼠标点击事件才能触发。
4. 鼠标移动到连接地址时,状态栏要显示确切地址。
这个几年前比较常常看到的网页特效,在状态栏写着日期或者"欢迎之类",其实这样的后果是访问者无法查看当前鼠标停放的url的地址。有可能造成触碰到钓鱼陷阱。
5. 无法打开边距小于100像素的窗口。
6. fileupload属性value值无法设置。
也许这个不好理解,但是如果可以设置的话,将有可能造成把你的某指定文件上传到服务器的后果。
7. 同源策略(The Same-Origin Policy)注意对于cookie也同样适用。
关于JavaScript跨站访问(Cross-Site)
跨域访问的问题在于第7点,首先理解脚本本身的来源与同源策略不相关。即B域调用A域的脚本(通过script的src),可以完全的访问B域的文档内容。但如果访问A域或者C域的话同源策略就发挥作用。
同源指的是:
同协议,同域名并且同端口。缺一不可。
某些情况下,同源策略会显得太严谨了。但在安全性的前提下,我们还是可以有解决办法的。这些解决办法跟安全性框架没任何冲突,除非它是浏览器的一个漏洞。
1、主域相同,子域之间的访问。
使用document的domain属性,默认情况下存放装载文档的服务器主机名。
例如,现在两个域,home.example.com和developer.example.com。
这两个域之间想互相装载文档属性的话是完全符合需求的,可以把document.domain属性设置为"example.com"。另注意domain的值至少要有一个点号。
此时文档就有同源性可以互相访问了。
2、主域不相同。
目前有两种解决办法,但是归根到底还是通过proxy实现。
proxy的作用:
proxy位于文档本域,作用是用来访问远程的资源并且返回给JavaScript。
一、使用AJAX XMLHttpRequest.
使用JavaScript构造XMLHttpRequest请求然后通过proxy获取资源。
二、使用script标签.
通过使用script标签设置src为proxy获取资源,然后可以得到得到远程数据。
其实以上两种解决办法还是基于“发送请求的操作然后获取服务端的数据”。比较推荐使用AJAX,但直接使用script标记的话会很方便,并且门槛应该相对低点。
另,还有一些是关于iframe的解决办法和情况没考虑,但暂时没遇到,先不考虑了,而且觉得情景也比较少。
附上java写的Proxy代码(jsp)
<%@ page language="java" pageEncoding="UTF-8"%>
<%@ page import="java.util.Map,java.util.HashMap,java.util.Enumeration"%>
<%--
/**
* 为解决JavaScript跨域访问资源时,遇到因同源策略而造成访问资源失败的问题。
* (而实际上,使用AJAX的XMLHttpRequest时碰到这种情况的几率居多。)
*
* 使用此代理可以实现请求转发:
* 把跨域的资源信息获取回本域,然后再把该信息响应给本域提交请求的对象。
*
* 中文编码问题: 跨域资源返回时都做了decode操作,默认UTF8编码。
*
* 参数问题: 访问此代理的参数中必须含有url的参数,作为跨域访问的目标url对象。
* 除了url参数外的其他参数,将生成为跨域访问时目标url的参数,共同转发过去。
*
* @author KennyLee E-mail:[email protected] 2009-2-25
*
*/
--%>
<%
String url = null;
Map<String, String> req_map = new HashMap<String, String>();
Enumeration<?> _enum = request.getParameterNames();
while (_enum.hasMoreElements()) {
String paramName = (String) _enum.nextElement();
String paramValue = request.getParameter(paramName);
req_map.put(paramName, paramValue);
}
if (!req_map.isEmpty())
url = req_map.remove("url");
if ((url != null) && (url.length() > 0)) {
if (!req_map.isEmpty()) {
StringBuffer url_sbf = new StringBuffer(url);
for (Map.Entry<String, String> entry : req_map.entrySet()) {//generate parameters
String _par_key = entry.getKey();
String _par_value = entry.getValue();
if (_par_key != null && _par_key != "") {
if (url_sbf.indexOf("?") == -1)
url_sbf.append("?");
else
url_sbf.append("&");
url_sbf.append(_par_key).append("=").append(
_par_value);
}
}
url = url_sbf.toString();
}
java.net.URL _url = new java.net.URL(url);
java.net.URLConnection urlcon = _url.openConnection();
java.io.InputStream is = urlcon.getInputStream();
java.io.BufferedReader buffer = new java.io.BufferedReader(
new java.io.InputStreamReader(is));
StringBuffer bs = new StringBuffer();
String lineStr = null;
while ((lineStr = buffer.readLine()) != null) {
String stri = java.net.URLDecoder.decode(lineStr, "UTF-8");
bs.append(stri).append("\n");
}
if (bs.toString().indexOf("<?xml version=") != -1) {//if XML file, for AJAX
response.setContentType("text/xml; charset=UTF-8");
response.setHeader("Cache-Control", "no-cache");
out.println(bs.toString());
} else
out.println(bs.toString());
}
%>