前面我们谈的大多是服务端与客户端的技术,服务器开发其实有时还会涉及到跨服务器的访问,比如腾讯的拍拍服务器需要知道登录的会员信息,
就需要访问会员服务器。
跨务器访问会涉及到很多的技术,比如访问权限控制,数据同步等,这里主要来学习一下传输层。
为了更容易理解,我们将访问端服务器称为客户端,被访问端服务器称为服务端。
客户端发起一个连接的过程:
socket_fd = socket( AF_INET,SOCK_STREAM,0 );
ret = connect( socket_fd, (sockaddr*)&addr, sizeof(addr) );
问题就出在第二步,connect 如果是同步的,如果服务端很忙,客户端就会一直阻塞在这里,最多有可能会耗上几十秒,开玩笑,不会吧,客户端也是个服务器唉,
敢情这块不就成了瓶颈吗。
我们来理清下思路:
就像你跟人家姑娘表白了,姑娘说今天不告诉你答案,明天打电话再告诉你,你是不是要一直不吃不喝在她家楼下等呢,还是回去好好休息等她电话。
够明白了么,我建议还是回家休息去吧,因为你还有很多很重要的事做呢。
同样的思路,客户端需要想办法把这个差事放进某个通知队列中。
解决这个问题就是在connect 前先要将 socket_fd 设为异步的(nonblocking):
fcntl(socket_fd, F_SETFL, fcntl(s, F_GETFL) | O_NONBLOCK);
连接的时候如果暂时连不上,connect 返回-1,好家伙,人家姑娘害羞呢,但又不表示拒绝。
那行吧,在家等电话,先要把人家姑娘号码存起来吧。
在咱们这里就是将 socket_fd 放到通知队列中,以epoll 为例,
struct epoll_event ee;
ee.events = EPOLLOUT | EPOLLET;
ee.data.fd = socket_fd;
epoll_ctl( epfd, EPOLL_CTL_ADD, socket_fd, &ee );
现在该干什么干什么去吧。
电话来了, 再做相应处理,应该高兴还是悲伤,表情函数预备下,免得到时大脑真空。
不同的是如果姑娘告诉你她答应你了事情就算完了,而epoll 回调告诉你有事件了你还需要确认一下:
getsockopt(socket_fd SOL_SOCKET, SO_ERROR, &error, (socklen_t *)&len);
if( !error )
{
//OK
}
或者也可以使用 getsockname或getpeername,确认OK了才表示连接成功了。
默认情况下,tomcat使用的的编码方式:iso8859-1
修改tomcat下的conf/server.xml文件
找到如下代码:
<Connector port="8080" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" />
这段代码规定了Tomcat监听HTTP请求的端口号等信息。
可以在这里添加一个属性:URIEncoding,将该属性值设置为UTF-8,即可让Tomcat(默认ISO-8859-1编码)以UTF-8的编码处理get请求。
修改完成后:
<Connector port="8080" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" URIEncoding="UTF-8" />
首先,用firefox浏览器开启firebug,打开新浪微博登陆首页,在网络状态下找到 http://login.sina.com.cn/sso/prelogin.php?entry=weibo&callback=sinaSSOController.preloginCallBack&su=&rsakt=mod&client=ssologin.js(v1.4.5)&_=1363935021054, 这条链接里面携带重要的信息,servertime,nonce,pubkey这些参数都是加密的重要信息。
sinaSSOController.preloginCallBack(
{"retcode":0,
"servertime":1363935070,
"pcid":"gz-454de2caeb7e34b78681bbfddb4b25da343c",
"nonce":"9Z70SO",
"pubkey":"EB2A38568661887FA180BDDB5CABD5F21C7BFD59C090CB2D245A87AC253062882729293E5506350508E7F9AA3BB77F4333231490F915F6D63C55FE2F08A49B353F444AD3993CACC02DB784ABBB8E42A9B1BBFFFB38BE18D78E87A0E41B9B8F73A928EE0CCEE1F6739884B9777E4FE9E88A1BBE495927AC4A799B3181D6442443","rsakv":"1330428213","exectime":1})
其中pubkey就是现在新浪微博的加密公钥。再找到 http://tjs.sjs.sinajs.cn/t5/register/js/page/login/index.js?version=201303221451 下载这条登陆js,我们在js中可以找到这段代码
if (a.loginType & z && a.servertime && sinaSSOEncoder && sinaSSOEncoder.RSAKey) {
e.servertime = a.servertime;
e.nonce = a.nonce;
e.pwencode = "rsa2";
e.rsakv = a.rsakv;
var f = new sinaSSOEncoder.RSAKey;
f.setPublic(a.rsaPubkey, "10001");
c = f.encrypt([a.servertime, a.nonce].join("\t") + "\n" + c)
} else if (a.loginType & A && a.servertime && sinaSSOEncoder && sinaSSOEncoder.hex_sha1) {
e.servertime = a.servertime;
e.nonce = a.nonce;
e.pwencode = "wsse";
c = sinaSSOEncoder.hex_sha1("" + sinaSSOEncoder.hex_sha1(sinaSSOEncoder.hex_sha1(c)) + a.servertime + a.nonce)
}以前的满足条件的加密过程是:
c = sinaSSOEncoder.hex_sha1("" + sinaSSOEncoder.hex_sha1(sinaSSOEncoder.hex_sha1(c)) + a.servertime + a.nonce)执行这句代码的加密方式有前辈已经贴出
http://blog.csdn.net/wolfphantasms/article/details/7398260(hex_sha1 ,三次hex_sha1加密过程)
而现在使用的加密过程是:
c = f.encrypt([a.servertime, a.nonce].join("\t") + "\n" + c)
因此只要在登陆js中提炼出RSA的加密函数就ok了。只要在java中调用js代码来加密登陆密码,至于servertime,nonce都可以自己模拟生成。下面是我产生servertime,nonce、和加密密码的方法:
private String encodeAccount(String account) {
String userName = "";
try {
userName = Base64.encodeBase64String(URLEncoder.encode(account,
"UTF-8").getBytes());
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return userName;
}
public String makeNonce(int len) {
String x = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
String str = "";
for (int i = 0; i < len; i++) {
str += x.charAt((int) (Math.ceil(Math.random() * 1000000) % x
.length()));
}
return str;
}
public String RSAencode(String publickey, String servertime,
String nonce, String pw) {
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("javascript");
String root = System.getProperty("user.dir");
logger.debug(root);
/*
* 指定加密RSA加密文件
*/
String jsFileName = root + "/src/main/js/sinaRSA.js";
/*
* 读取js文件
*/
FileReader reader;
String pass = "";
try {
reader = new FileReader(jsFileName);
engine.eval(reader);
if (engine instanceof Invocable) {
Invocable invoke = (Invocable) engine;
// 调用encrypt方法,并传入密码加密
pass = invoke.invokeFunction("sinaRsa", publickey.trim(),
servertime.trim(), nonce.trim(), pw.trim()).toString();
logger.debug("after encode password = : " + pass);
return pass;
}
reader.close();
} catch (Exception e) {
logger.error("js load fail .... ", e);
}
return pass;
}
private String getServerTime() {
long servertime = new Date().getTime() / 1000;
return String.valueOf(servertime);
}登陆js如下:
function sinaRsa(pbkey, servertime, nonce, pw) {
var f = new sinaSSOEncoder.RSAKey;
f.setPublic(pbkey, "10001");
var psw = f.encrypt([servertime, nonce].join("\t") + "\n" + pw);
return psw;
}
var sinaSSOEncoder = sinaSSOEncoder || {};
(function() {
var a = 0, b = 8;
this.hex_sha1 = function(a) {
return i(c(h(a), a.length * b))
};
var c = function(a, b) {
a[b >> 5] |= 128 << 24 - b % 32;
a[(b + 64 >> 9 << 4) + 15] = b;
var c = Array(80), h = 1732584193, i = -271733879, j = -1732584194, k = 271733878, l = -1009589776;
for (var m = 0; m < a.length; m += 16) {
var n = h, o = i, p = j, q = k, r = l;
for (var s = 0; s < 80; s++) {
s < 16 ? c[s] = a[m + s] : c[s] = g(c[s - 3] ^ c[s - 8] ^ c[s - 14] ^ c[s - 16], 1);
var t = f(f(g(h, 5), d(s, i, j, k)), f(f(l, c[s]), e(s)));
l = k;
k = j;
j = g(i, 30);
i = h;
h = t
}
h = f(h, n);
i = f(i, o);
j = f(j, p);
k = f(k, q);
l = f(l, r)
}
return [h, i, j, k, l]
}, d = function(a, b, c, d) {
return a < 20 ? b & c | ~b & d : a < 40 ? b ^ c ^ d : a < 60 ? b & c | b & d | c & d : b ^ c ^ d
}, e = function(a) {
return a < 20 ? 1518500249 : a < 40 ? 1859775393 : a < 60 ? -1894007588 : -899497514
}, f = function(a, b) {
var c = (a & 65535) + (b & 65535), d = (a >> 16) + (b >> 16) + (c >> 16);
return d << 16 | c & 65535
}, g = function(a, b) {
return a << b | a >>> 32 - b
}, h = function(a) {
var c = [], d = (1 << b) - 1;
for (var e = 0; e < a.length * b; e += b)
c[e >> 5] |= (a.charCodeAt(e / b) & d) << 24 - e % 32;
return c
}, i = function(b) {
var c = a ? "0123456789ABCDEF" : "0123456789abcdef", d = "";
for (var e = 0; e < b.length * 4; e++)
d += c.charAt(b[e >> 2] >> (3 - e % 4) * 8 + 4 & 15) + c.charAt(b[e >> 2] >> (3 - e % 4) * 8 & 15);
return d
};
this.base64 = {
encode : function(a) {
a = "" + a;
if (a == "")
return "";
var b = "", c, d, e = "", f, g, h, i = "", j = 0;
do {
c = a.charCodeAt(j++);
d = a.charCodeAt(j++);
e = a.charCodeAt(j++);
f = c >> 2;
g = (c & 3) << 4 | d >> 4;
h = (d & 15) << 2 | e >> 6;
i = e & 63;
isNaN(d) ? h = i = 64 : isNaN(e) && ( i = 64);
b = b + this._keys.charAt(f) + this._keys.charAt(g) + this._keys.charAt(h) + this._keys.charAt(i);
c = d = e = "";
f = g = h = i = ""
} while(j<a.length);
return b
},
_keys : "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="
}
}).call(sinaSSOEncoder);
(function() {
function bt(a) {
var b = bp(a, this.n.bitLength() + 7 >> 3);
if (b == null)
return null;
var c = this.doPublic(b);
if (c == null)
return null;
var d = c.toString(16);
return (d.length & 1) == 0 ? d : "0" + d
}
function bs(a) {
return a.modPowInt(this.e, this.n)
}
function br(a, b) {
if (a != null && b != null && a.length > 0 && b.length > 0) {
this.n = bm(a, 16);
this.e = parseInt(b, 16)
} else
alert("Invalid RSA public key")
}
function bq() {
this.n = null;
this.e = 0;
this.d = null;
this.p = null;
this.q = null;
this.dmp1 = null;
this.dmq1 = null;
this.coeff = null
}
function bp(a, b) {
if (b < a.length + 11) {
alert("Message too long for RSA");
return null
}
var c = [], e = a.length - 1;
while (e >= 0 && b > 0) {
var f = a.charCodeAt(e--);
if (f < 128)
c[--b] = f;
else if (f > 127 && f < 2048) {
c[--b] = f & 63 | 128;
c[--b] = f >> 6 | 192
} else {
c[--b] = f & 63 | 128;
c[--b] = f >> 6 & 63 | 128;
c[--b] = f >> 12 | 224
}
}
c[--b] = 0;
var g = new bl, h = [];
while (b > 2) {
h[0] = 0;
while (h[0] == 0)
g.nextBytes(h);
c[--b] = h[0]
}
c[--b] = 2;
c[--b] = 0;
return new d(c)
}
function bo(a) {
return a < 16 ? "0" + a.toString(16) : a.toString(16)
}
function bn(a, b) {
var c = "