本文記錄一次基於 HttpClient 4.5 模擬新浪微博登錄的完整過程,重點解決 2016 年新浪採用的 RSA2 密碼加密、預登錄獲取參數、跨域 Cookie 同步 等關鍵難點。代碼僅供學習研究,請勿用於非法用途。
prelogin.php,獲取 servertime、nonce、pubkey、rsakv 等參數。javax.script 引擎執行新浪官方 JS(sinaSSOEncoder),調用 getpass(password, servertime, nonce, pubkey) 得到 RSA2 加密後的 sp。login.php,攜帶 su、sp、servertime、nonce、rsakv 等參數。crossDomainUrlList,依次 GET 這些 URL 同步 Cookie 到各業務域。https://login.sina.com.cn/member/my.php 確認登錄態。public class SinaWeiboLogger {
private HttpClient client;
private HttpGet get;
private HttpPost post;
private BasicCookieStore cookieStore;
private String username, password;
private String su, sp; // 加密後的用戶名/密碼
private long servertime;
private String nonce, pubkey, rsakv;
// ... 其他預登錄字段
// 完整的 JS 加密邏輯(sina_js)內嵌在類中,長度約 3000 行
private static final String sina_js = "var sinaSSOEncoder=...function getpass(pwd,servertime,nonce,rsaPubkey){...}";
public SinaWeiboLogger(String username, String password) {
this.username = username;
this.password = password;
// 配置 Cookie 策略,避免 "Cookie rejected"
Registry<CookieSpecProvider> rg = RegistryBuilder.<CookieSpecProvider>create()
.register("myCookie", ctx -> new DefaultCookieSpec()).build();
cookieStore = new BasicCookieStore();
client = HttpClients.custom()
.setDefaultCookieStore(cookieStore)
.setDefaultCookieSpecRegistry(rg).build();
get = new HttpGet();
post = new HttpPost();
}
private void preLogin() throws Exception {
su = new String(Base64.encodeBase64(URLEncoder.encode(username, "UTF-8").getBytes()));
String url = "https://login.sina.com.cn/sso/prelogin.php?entry=account&callback=sinaSSOController.preloginCallBack&su=" + su + "&rsakt=mod&client=ssologin.js(v1.4.15)&_=" + System.currentTimeMillis();
// 設置 Header、執行請求、解析 JSON 獲取參數...
}
private void accountLogin() throws Exception {
// 1. 執行 JS 加密密碼
ScriptEngine se = new ScriptEngineManager().getEngineByName("javascript");
se.eval(sina_js);
sp = (String) ((Invocable) se).invokeFunction("getpass", password, servertime, nonce, pubkey);
// 2. 構建 POST 參數
List<NameValuePair> params = new ArrayList<>();
params.add(new BasicNameValuePair("entry", "account"));
params.add(new BasicNameValuePair("gateway", "1"));
params.add(new BasicNameValuePair("su", su));
params.add(new BasicNameValuePair("servertime", String.valueOf(servertime)));
params.add(new BasicNameValuePair("nonce", nonce));
params.add(new BasicNameValuePair("pwencode", "rsa2"));
params.add(new BasicNameValuePair("rsakv", rsakv));
params.add(new BasicNameValuePair("sp", sp));
// ... 其他固定參數
post.setEntity(new UrlEncodedFormEntity(params, "UTF-8"));
// 執行請求、解析返回、跨域回調...
}
public void login() {
try { preLogin(); accountLogin(); }
catch (Exception e) { e.printStackTrace(); }
}
}
.js 文件,運行時讀取,避免硬編碼。完整代碼已上傳 GitHub(鏈接略),可直接下載調試。