原文地址:
http://www.java2000.net/p7715
http://blog.csdn.net/java2000_net/archive/2008/07/30/2736659.aspx
此文的版权归JAVA世纪网(www.java2000.net)和CSDN(www.csdn.net)所有,转载请保留此声明、代码注释和原始链接
CSDN在很早以前就提供了登录CSDN的OpenAPI,原始地址如下:http://passport.csdn.net/WebService/UserLoginService.asmx
原理分析
系统提供了2个方法
UserLogin
UserPreLogin
我们要使用这个服务,必须经过2个步骤。
1 预登录,获取一些验证信息(ClientKey)以及验证码图片
2 显示验证码图片,并让用户输入用户名和密码
3 提交数据,包括ClientKey,用户名和密码,以及验证码
4 拿到结果,最重要的就是 cookie的信息(ErrorInfo)
我们依然使用
URLConnection直接进行数据处理的方法.
先看预登录的部分。
看看协议
POST /WebService/UserLoginService.asmx HTTP/1.1
Host: passport.csdn.net
Content-Type: application/soap+xml; charset=utf-8
Content-Length: length
<?xml version="1.0" encoding="utf-8"?>
<soap12:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap12="http://www.w3.org/2003/05/soap-envelope">
<soap12:Body>
<UserPreLogin xmlns="http://passport.csdn.net/">
<RobotName>string</RobotName>
</UserPreLogin>
</soap12:Body>
</soap12:Envelope>
实现的代码
/**
* 预登录CSDN系统
*
* @author 赵学庆,www.java2000.net
* @param username 预登录的用户名
* @return 结果数组.[0]:验证码的字符串, [1] Client的字符串 ,[2] Cookie数据
*/
public static String[] preLogin(String username) {
StringBuilder b = new StringBuilder();
try {
b.append("<?xml version=/"1.0/" encoding=/"utf-8/"?>/n");
b
.append("<soap12:Envelope xmlns:xsi=/"http://www.w3.org/2001/XMLSchema-instance/" xmlns:xsd=/"http://www.w3.org/2001/XMLSchema/" xmlns:soap12=/"http://www.w3.org/2003/05/soap-envelope/">/n");
b.append(" <soap12:Body>/n");
b.append(" <UserPreLogin xmlns=/"http://passport.csdn.net//">/n");
b.append(" <RobotName>" + username + "</RobotName>/n");
b.append(" </UserPreLogin>/n");
b.append(" </soap12:Body>/n");
b.append("</soap12:Envelope>/n");
String[] strs = postPage("http://passport.csdn.net/WebService/UserLoginService.asmx",
"passport.csdn.net", null, b.toString());
String textXml = strs[1];
SAXBuilder builder = new SAXBuilder();
Document doc = null;
Reader in = new StringReader(textXml);
doc = builder.build(in);
Element root = doc.getRootElement();
List ls = root.getChildren();// 注意此处取出的是root节点下面的一层的Element集合
Element body = (Element) ls.get(0);
Element response = (Element) body.getChildren().get(0);
List content = response.getChildren();
Element result = (Element) content.get(0);
Element result2 = (Element) content.get(1);
String[] rtn = new String[3];
if ("UserPreLoginResult".equals(result.getName())) {
rtn[0] = result.getText();
}
if ("ClientKey".equals(result2.getName())) {
rtn[1] = result2.getText();
}
rtn[2] = strs[0];
return rtn;
} catch (Exception ex) {
ex.printStackTrace();
return null;
}
}
调用的登录表单代码
<form method="post">
<table>
<tr>
<td>CSDN用户名</td>
<td><input type="text" name="username" /></td>
</tr>
<tr>
<td>CSDN密码</td>
<td><input type="password" name="password" /></td>
</tr>
<tr>
<td>图形验证码</td>
<td><input type="text" size="6" name="code" /><br>
<img id="vc" src="getCSDNImage.jsp" /></td>
</tr>
<tr>
<th>保存密码</th>
<td><input type="checkbox" name="savePassword" value="true" id="savePassword" tabindex="-1" /></td>
</tr>
<tr>
<td colspan="2">
<button type="submit">登陆</button>
</td>
</tr>
</table>
</form>
其中的显示验证码的部分是关键
String[] rtn = OpenAPI.preLogin("java2000_net_test");
session.setAttribute("CSDN_ClientKey",rtn[1]);
session.setAttribute("CSDN_COOKIE",rtn[2]);
OutputStream os = response.getOutputStream();
os.write( org.apache.commons.codec.binary.Base64.decodeBase64(rtn[0].getBytes()));
里面讲返回的信息记录到了session里面,以备下次调用时使用
提示:返回的验证码图片数据是Base64编码后的,需要先解码才能使用。
到目前为止,预登录完成
下面我们进入第二步,登录
先看登录的协议
POST /WebService/UserLoginService.asmx HTTP/1.1
Host: passport.csdn.net
Content-Type: application/soap+xml; charset=utf-8
Content-Length: length
<?xml version="1.0" encoding="utf-8"?>
<soap12:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap12="http://www.w3.org/2003/05/soap-envelope">
<soap12:Body>
<UserLogin xmlns="http://passport.csdn.net/">
<LoginName>string</LoginName>
<Password>string</Password>
<VerifyCode>string</VerifyCode>
<ClientKey>string</ClientKey>
</UserLogin>
</soap12:Body>
</soap12:Envelope>
参数有4个,登录的用户名,密码,认证码和ClientKey
登录返回的XML协议
HTTP/1.1 200 OK
Content-Type: application/soap+xml; charset=utf-8
Content-Length: length
<?xml version="1.0" encoding="utf-8"?>
<soap12:Envelopexmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:xsd="http://www.w3.org/2001/XMLSchema"xmlns:soap12="http://www.w3.org/2003/05/soap-envelope">
<soap12:Body>
<UserLoginResponse xmlns="http://passport.csdn.net/">
<UserLoginResult>boolean</UserLoginResult>
<ErrorInfo>string</ErrorInfo>
<WarningInfo>string</WarningInfo>
</UserLoginResponse>
</soap12:Body>
</soap12:Envelope>
其中的
UserLoginResult为登录结果
ErrorInfo 为登录失败的错误信息
WarningInfo 我没用,估计是不是被暴力破解的那个警告啊
实现的代码
/**
* 登录CSDN系统
*
* @author 赵学庆,www.java2000.net
* @param username 登录的用户名
* @param password 密码
* @param verifyCode 校验码
* @param clientkey 预登录的clientKey
* @param cookie 预登录拿到的cookie
* @return 登录结果,[0] 登录结果,'true'成功, [1] 错误信息,[2] 登录后的cookie数据
*/
public static String[] login(String username, String password, String verifyCode,
String clientkey, String cookie) {
StringBuilder b = new StringBuilder();
try {
b.append("<?xml version=/"1.0/" encoding=/"utf-8/"?>/n");
b
.append("<soap12:Envelope xmlns:xsi=/"http://www.w3.org/2001/XMLSchema-instance/" xmlns:xsd=/"http://www.w3.org/2001/XMLSchema/" xmlns:soap12=/"http://www.w3.org/2003/05/soap-envelope/">/n");
b.append(" <soap12:Body>/n");
b.append(" <UserLogin xmlns=/"http://passport.csdn.net//">/n");
b.append(" <LoginName>" + username + "</LoginName>/n");
b.append(" <Password>" + password + "</Password>/n");
b.append(" <VerifyCode>" + verifyCode + "</VerifyCode>/n");
b.append(" <ClientKey>" + clientkey + "</ClientKey>/n");
b.append(" </UserLogin>/n");
b.append(" </soap12:Body>/n");
b.append(" </soap12:Envelope>/n");
String[] strs = postPage("http://passport.csdn.net/WebService/UserLoginService.asmx",
"passport.csdn.net", cookie, b.toString());
String textXml = strs[1];
SAXBuilder builder = new SAXBuilder();
Document doc = null;
Reader in = new StringReader(textXml);
doc = builder.build(in);
Element root = doc.getRootElement();
List ls = root.getChildren();// 注意此处取出的是root节点下面的一层的Element集合
Element body = (Element) ls.get(0);
Element response = (Element) body.getChildren().get(0);
List content = response.getChildren();
Element result = (Element) content.get(0);
Element result2 = (Element) content.get(1);
String[] rtn = new String[3];
if ("UserLoginResult".equals(result.getName())) {
rtn[0] = result.getText();
}
if ("ErrorInfo".equals(result2.getName())) {
rtn[1] = result2.getText();
}
rtn[2] = strs[0];
return rtn;
} catch (Exception ex) {
ex.printStackTrace();
return null;
}
}
下面看登录的程序
String username = request.getParameter("username");
String password = request.getParameter("password");
String code = request.getParameter("code");
String clientKey = (String) session.getAttribute("CSDN_ClientKey");
String cookies = (String) session.getAttribute("CSDN_COOKIE");
String[] rtn = OpenAPI.login(username, password, code, clientKey, cookies);
if ("true".equals(rtn[0])) {
out.println("登陆成功!");
// 其它的处理代码
return;
} else {
out.println("登陆失败:" + rtn[1]);
}
总结:
这个OpenAPI设计的还是挺好的,我们拿到ClientKey 和 Cookie之后,就可以在其它的请求里面把他们放在cookie里,直接向CSDN提交请求,就如同你用浏览器直接访问一样。
附件:
系统用到的postPage的源代码
/**
* 提交数据到指定的地址
*
* @param urlTo 指定提交的地址
* @param host 主机名称
* @param cookies cookies的数据
* @param data 提交的数据
* @return 返回提交的结果数组,[0] 为返回的cookie 数据,[1]为返回的内容本体数据
*/
private static String[] postPage(String urlTo, String host, String cookies, String data) {
try {
URL url = new URL(urlTo);
HttpURLConnection con = (HttpURLConnection) url.openConnection();
con.setDoOutput(true); // POST方式
con.setRequestMethod("POST");
con.addRequestProperty("Host", host);
con.addRequestProperty("Content-Type", "application/soap+xml; charset=utf-8");
if (cookies != null) {
con.addRequestProperty("Cookie", cookies);
}
byte[] bs = data.getBytes("UTF-8");
con.addRequestProperty("Content-Length", Integer.toString(bs.length));
OutputStream os = con.getOutputStream(); // 输出流,写数据
os.write(bs);
Map<String, List<String>> map = con.getHeaderFields();
String[] rtn = new String[2];
String cookie = "";
if (map.get("Set-Cookie") != null)
for (String v : map.get("Set-Cookie")) {
cookie += v.substring(0, v.indexOf(";") + 1);
}
rtn[0] = cookie;
BufferedReader reader = new BufferedReader(new InputStreamReader(con.getInputStream(),
"UTF-8")); // 读取结果
String line;
StringBuilder b = new StringBuilder();
while ((line = reader.readLine()) != null) {
b.append(line);
}
reader.close();
rtn[1] = b.toString();
return rtn;
} catch (Exception ex) {
ex.printStackTrace();
return null;
}
}
<script type="text/javascript"> </script> <script type="text/javascript" src="http://pagead2.googlesyndication.com/pagead/show_ads.js"> </script>
http://www.java2000.net/p7715
http://blog.csdn.net/java2000_net/archive/2008/07/30/2736659.aspx
此文的版权归JAVA世纪网(www.java2000.net)和CSDN(www.csdn.net)所有,转载请保留此声明、代码注释和原始链接
CSDN在很早以前就提供了登录CSDN的OpenAPI,原始地址如下:http://passport.csdn.net/WebService/UserLoginService.asmx
原理分析
系统提供了2个方法
UserLogin
UserPreLogin
我们要使用这个服务,必须经过2个步骤。
1 预登录,获取一些验证信息(ClientKey)以及验证码图片
2 显示验证码图片,并让用户输入用户名和密码
3 提交数据,包括ClientKey,用户名和密码,以及验证码
4 拿到结果,最重要的就是 cookie的信息(ErrorInfo)
我们依然使用
URLConnection直接进行数据处理的方法.
先看预登录的部分。
看看协议
POST /WebService/UserLoginService.asmx HTTP/1.1
Host: passport.csdn.net
Content-Type: application/soap+xml; charset=utf-8
Content-Length: length
<?xml version="1.0" encoding="utf-8"?>
<soap12:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap12="http://www.w3.org/2003/05/soap-envelope">
<soap12:Body>
<UserPreLogin xmlns="http://passport.csdn.net/">
<RobotName>string</RobotName>
</UserPreLogin>
</soap12:Body>
</soap12:Envelope>
实现的代码
/**
* 预登录CSDN系统
*
* @author 赵学庆,www.java2000.net
* @param username 预登录的用户名
* @return 结果数组.[0]:验证码的字符串, [1] Client的字符串 ,[2] Cookie数据
*/
public static String[] preLogin(String username) {
StringBuilder b = new StringBuilder();
try {
b.append("<?xml version=/"1.0/" encoding=/"utf-8/"?>/n");
b
.append("<soap12:Envelope xmlns:xsi=/"http://www.w3.org/2001/XMLSchema-instance/" xmlns:xsd=/"http://www.w3.org/2001/XMLSchema/" xmlns:soap12=/"http://www.w3.org/2003/05/soap-envelope/">/n");
b.append(" <soap12:Body>/n");
b.append(" <UserPreLogin xmlns=/"http://passport.csdn.net//">/n");
b.append(" <RobotName>" + username + "</RobotName>/n");
b.append(" </UserPreLogin>/n");
b.append(" </soap12:Body>/n");
b.append("</soap12:Envelope>/n");
String[] strs = postPage("http://passport.csdn.net/WebService/UserLoginService.asmx",
"passport.csdn.net", null, b.toString());
String textXml = strs[1];
SAXBuilder builder = new SAXBuilder();
Document doc = null;
Reader in = new StringReader(textXml);
doc = builder.build(in);
Element root = doc.getRootElement();
List ls = root.getChildren();// 注意此处取出的是root节点下面的一层的Element集合
Element body = (Element) ls.get(0);
Element response = (Element) body.getChildren().get(0);
List content = response.getChildren();
Element result = (Element) content.get(0);
Element result2 = (Element) content.get(1);
String[] rtn = new String[3];
if ("UserPreLoginResult".equals(result.getName())) {
rtn[0] = result.getText();
}
if ("ClientKey".equals(result2.getName())) {
rtn[1] = result2.getText();
}
rtn[2] = strs[0];
return rtn;
} catch (Exception ex) {
ex.printStackTrace();
return null;
}
}
调用的登录表单代码
<form method="post">
<table>
<tr>
<td>CSDN用户名</td>
<td><input type="text" name="username" /></td>
</tr>
<tr>
<td>CSDN密码</td>
<td><input type="password" name="password" /></td>
</tr>
<tr>
<td>图形验证码</td>
<td><input type="text" size="6" name="code" /><br>
<img id="vc" src="getCSDNImage.jsp" /></td>
</tr>
<tr>
<th>保存密码</th>
<td><input type="checkbox" name="savePassword" value="true" id="savePassword" tabindex="-1" /></td>
</tr>
<tr>
<td colspan="2">
<button type="submit">登陆</button>
</td>
</tr>
</table>
</form>
其中的显示验证码的部分是关键
String[] rtn = OpenAPI.preLogin("java2000_net_test");
session.setAttribute("CSDN_ClientKey",rtn[1]);
session.setAttribute("CSDN_COOKIE",rtn[2]);
OutputStream os = response.getOutputStream();
os.write( org.apache.commons.codec.binary.Base64.decodeBase64(rtn[0].getBytes()));
里面讲返回的信息记录到了session里面,以备下次调用时使用
提示:返回的验证码图片数据是Base64编码后的,需要先解码才能使用。
到目前为止,预登录完成
下面我们进入第二步,登录
先看登录的协议
POST /WebService/UserLoginService.asmx HTTP/1.1
Host: passport.csdn.net
Content-Type: application/soap+xml; charset=utf-8
Content-Length: length
<?xml version="1.0" encoding="utf-8"?>
<soap12:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap12="http://www.w3.org/2003/05/soap-envelope">
<soap12:Body>
<UserLogin xmlns="http://passport.csdn.net/">
<LoginName>string</LoginName>
<Password>string</Password>
<VerifyCode>string</VerifyCode>
<ClientKey>string</ClientKey>
</UserLogin>
</soap12:Body>
</soap12:Envelope>
参数有4个,登录的用户名,密码,认证码和ClientKey
登录返回的XML协议
HTTP/1.1 200 OK
Content-Type: application/soap+xml; charset=utf-8
Content-Length: length
<?xml version="1.0" encoding="utf-8"?>
<soap12:Envelopexmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:xsd="http://www.w3.org/2001/XMLSchema"xmlns:soap12="http://www.w3.org/2003/05/soap-envelope">
<soap12:Body>
<UserLoginResponse xmlns="http://passport.csdn.net/">
<UserLoginResult>boolean</UserLoginResult>
<ErrorInfo>string</ErrorInfo>
<WarningInfo>string</WarningInfo>
</UserLoginResponse>
</soap12:Body>
</soap12:Envelope>
其中的
UserLoginResult为登录结果
ErrorInfo 为登录失败的错误信息
WarningInfo 我没用,估计是不是被暴力破解的那个警告啊
实现的代码
/**
* 登录CSDN系统
*
* @author 赵学庆,www.java2000.net
* @param username 登录的用户名
* @param password 密码
* @param verifyCode 校验码
* @param clientkey 预登录的clientKey
* @param cookie 预登录拿到的cookie
* @return 登录结果,[0] 登录结果,'true'成功, [1] 错误信息,[2] 登录后的cookie数据
*/
public static String[] login(String username, String password, String verifyCode,
String clientkey, String cookie) {
StringBuilder b = new StringBuilder();
try {
b.append("<?xml version=/"1.0/" encoding=/"utf-8/"?>/n");
b
.append("<soap12:Envelope xmlns:xsi=/"http://www.w3.org/2001/XMLSchema-instance/" xmlns:xsd=/"http://www.w3.org/2001/XMLSchema/" xmlns:soap12=/"http://www.w3.org/2003/05/soap-envelope/">/n");
b.append(" <soap12:Body>/n");
b.append(" <UserLogin xmlns=/"http://passport.csdn.net//">/n");
b.append(" <LoginName>" + username + "</LoginName>/n");
b.append(" <Password>" + password + "</Password>/n");
b.append(" <VerifyCode>" + verifyCode + "</VerifyCode>/n");
b.append(" <ClientKey>" + clientkey + "</ClientKey>/n");
b.append(" </UserLogin>/n");
b.append(" </soap12:Body>/n");
b.append(" </soap12:Envelope>/n");
String[] strs = postPage("http://passport.csdn.net/WebService/UserLoginService.asmx",
"passport.csdn.net", cookie, b.toString());
String textXml = strs[1];
SAXBuilder builder = new SAXBuilder();
Document doc = null;
Reader in = new StringReader(textXml);
doc = builder.build(in);
Element root = doc.getRootElement();
List ls = root.getChildren();// 注意此处取出的是root节点下面的一层的Element集合
Element body = (Element) ls.get(0);
Element response = (Element) body.getChildren().get(0);
List content = response.getChildren();
Element result = (Element) content.get(0);
Element result2 = (Element) content.get(1);
String[] rtn = new String[3];
if ("UserLoginResult".equals(result.getName())) {
rtn[0] = result.getText();
}
if ("ErrorInfo".equals(result2.getName())) {
rtn[1] = result2.getText();
}
rtn[2] = strs[0];
return rtn;
} catch (Exception ex) {
ex.printStackTrace();
return null;
}
}
下面看登录的程序
String username = request.getParameter("username");
String password = request.getParameter("password");
String code = request.getParameter("code");
String clientKey = (String) session.getAttribute("CSDN_ClientKey");
String cookies = (String) session.getAttribute("CSDN_COOKIE");
String[] rtn = OpenAPI.login(username, password, code, clientKey, cookies);
if ("true".equals(rtn[0])) {
out.println("登陆成功!");
// 其它的处理代码
return;
} else {
out.println("登陆失败:" + rtn[1]);
}
总结:
这个OpenAPI设计的还是挺好的,我们拿到ClientKey 和 Cookie之后,就可以在其它的请求里面把他们放在cookie里,直接向CSDN提交请求,就如同你用浏览器直接访问一样。
附件:
系统用到的postPage的源代码
/**
* 提交数据到指定的地址
*
* @param urlTo 指定提交的地址
* @param host 主机名称
* @param cookies cookies的数据
* @param data 提交的数据
* @return 返回提交的结果数组,[0] 为返回的cookie 数据,[1]为返回的内容本体数据
*/
private static String[] postPage(String urlTo, String host, String cookies, String data) {
try {
URL url = new URL(urlTo);
HttpURLConnection con = (HttpURLConnection) url.openConnection();
con.setDoOutput(true); // POST方式
con.setRequestMethod("POST");
con.addRequestProperty("Host", host);
con.addRequestProperty("Content-Type", "application/soap+xml; charset=utf-8");
if (cookies != null) {
con.addRequestProperty("Cookie", cookies);
}
byte[] bs = data.getBytes("UTF-8");
con.addRequestProperty("Content-Length", Integer.toString(bs.length));
OutputStream os = con.getOutputStream(); // 输出流,写数据
os.write(bs);
Map<String, List<String>> map = con.getHeaderFields();
String[] rtn = new String[2];
String cookie = "";
if (map.get("Set-Cookie") != null)
for (String v : map.get("Set-Cookie")) {
cookie += v.substring(0, v.indexOf(";") + 1);
}
rtn[0] = cookie;
BufferedReader reader = new BufferedReader(new InputStreamReader(con.getInputStream(),
"UTF-8")); // 读取结果
String line;
StringBuilder b = new StringBuilder();
while ((line = reader.readLine()) != null) {
b.append(line);
}
reader.close();
rtn[1] = b.toString();
return rtn;
} catch (Exception ex) {
ex.printStackTrace();
return null;
}
}
<script type="text/javascript"> </script> <script type="text/javascript" src="http://pagead2.googlesyndication.com/pagead/show_ads.js"> </script>