前面我们谈的大多是服务端与客户端的技术,服务器开发其实有时还会涉及到跨服务器的访问,比如腾讯的拍拍服务器需要知道登录的会员信息,
就需要访问会员服务器。
跨务器访问会涉及到很多的技术,比如访问权限控制,数据同步等,这里主要来学习一下传输层。
为了更容易理解,我们将访问端服务器称为客户端,被访问端服务器称为服务端。
客户端发起一个连接的过程:
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 = "