首页 > 代码库 > Android端不通过登录SDK实现QQ登录验证
Android端不通过登录SDK实现QQ登录验证
通常的APP如果要调用QQ登录验证,需要使用QQ提供的登录SDK,但是QQ的SDK使用需要申请APP KEY和APPID,还要向腾讯提交比较繁琐的其它申请,而且如果通过该方法,登录信息全部被QQ掌握。那么是否有方法可以不申请DIY使用QQ登录验证呢。
看了几个QQ移动产品,并未使用这种方式APP KEY的方式进行验证登录,大致确定QQ的产品并没有使用这种接口,而是使用自有的方法直接登录。于是找了一款产品分析了一下。
首先确认了,登录的功能是由oicq这个包来负责的。
接下来分析它的调用接口,通过调试跟踪,确认了一登录过程都是在一个类中完成的。关键登录过程如下图:
第一步:通过QQ号和密码,发出登录请求
第二步:因为登录请求会有网络响应延时,接下来要做个登录响应监听,继承的监听类如下图:
第三步:监听响应后,就可以获取相关的登录返回信息了
WloginSimpleInfo类存放返回信息中的几个比较基本的信息,像年龄、头像ID、性别、昵称等等,如下图:
基本上登录过程就是上面三步所示,实际代码略嫌啰嗦。
那么要实现登录验证的功能,首先要使用oicq包,然后要把oicq的调用接口自已实现出来。
一种方法是把oicq包的代码实现出来,用jd还原的代码尝试了一下,发现工作量超大,于是转换思路,寻找更为简便的方法。想到dex可以无损转换成jar包,那能不能直接把oicq文件 夹扣出来,做成jar包调用呢。
实践了一下,发现确实可行,步骤如下:
1.把classes.dex转换成XXXX.jar
2.把XXXX.jar文件的扩展名改成zip,获取XXXX.zip文件
3.用winrar打开XXXX.zip文件,把除了oicq以外的文件夹全部删掉,保存压缩文件为副本oicq.jar
这样获得的jar文件,可以做为第三方包导入到工程里
剩下的就是实现oicq的调用接口了,把前面三步的逆向代码整理,简化了一下,添加两个类
[java] view plaincopy
public class QQLogin {
private Context mContext;
private QQLoginListener nListener;
WtloginHelper wh;
int userAge = 0;
int gender = 0;
String nickName = "";
Handler notifyHandler;
public QQLogin(Context paramContext,Handler msgHandler)
{
mContext = paramContext;
nListener = new QQLoginListener(this);
notifyHandler = msgHandler;
}
public int getUserAge()
{
return userAge;
}
public String getUserNickname()
{
return nickName;
}
public void getUserInfo(String userAccount, WUserSigInfo userSigInfo)
{
Log.v("getUserInfo","in:"+userAccount);
if(userAccount != null ) {
WloginSimpleInfo simpleInfo = new WloginSimpleInfo();
Boolean v2 = wh.GetBasicUserInfo(userAccount, simpleInfo);
userAge = (simpleInfo._age[0]& 0xFF);
gender = (simpleInfo._gander[0]& 0xFF);
Log.v("gander",gender+"");
nickName = new String(simpleInfo._nick);
Log.v("age",userAge + "");
Log.v("nick",nickName);
}
else
{
Log.v("getUserInfo","account null");
}
notifyHandler.sendEmptyMessage(1);
}
public void LoginError()
{
notifyHandler.sendEmptyMessage(2);
}
//Set Login QQ Num and Password
//Set Login CallBack
public void SetLogin(String username,String passwd)
{
Log.e("SetLogin","start");
wh = new WtloginHelper(mContext);
wh.SetTimeOut(0);
wh.SetListener(nListener);
wh.SetAppClientVersion(4010010);
WUserSigInfo usi = new WUserSigInfo();
wh.GetStWithPasswd(username, 83886593, passwd, usi);
}
static void onGetStWithPasswd(QQLogin nl, String userAccount, WUserSigInfo userSigInfo, int ret, ErrMsg errMsg) {
Log.e("onGetStWithPasswd",userAccount);
nl.onGetStWithPasswd(userAccount, userSigInfo, ret, errMsg);
}
private void onGetStWithPasswd(String userAccount, WUserSigInfo userSigInfo, int ret, ErrMsg errMsg) {
switch(ret) {
//login success
case 0: {
Log.e("getUserInfo",userAccount);
getUserInfo(userAccount, userSigInfo);
break;
}
//login error
default:{
LoginError();
break;
}
}
}
}
下面是监听类:
[java] view plaincopy
import oicq.wlogin_sdk.request.WUserSigInfo;
import oicq.wlogin_sdk.request.WtloginListener;
import oicq.wlogin_sdk.tools.ErrMsg;
class QQLoginListener extends WtloginListener {
private QQLogin nl;
public QQLoginListener(QQLogin arg1) {
nl = arg1;
}
public void OnCheckPictureAndGetSt(String arg2, byte[] arg3, WUserSigInfo arg4, int arg5, ErrMsg
arg6) {
}
public void OnException(ErrMsg arg5, int arg6, WUserSigInfo arg7) {
}
//login with password callback
public void OnGetStWithPasswd(String userAccount, long dwAppid, int dwMainSigMap, long dwSubDstAppid, String userPasswd, WUserSigInfo
userSigInfo, int ret, ErrMsg errMsg) {
QQLogin.onGetStWithPasswd(nl, userAccount, userSigInfo, ret, errMsg);
}
public void OnGetStWithoutPasswd(String arg2, long arg3, long arg5, int arg7, long arg8, WUserSigInfo
arg10, int arg11, ErrMsg arg12) {
}
public void OnRefreshPictureData(String arg4, WUserSigInfo arg5, byte[] arg6, int arg7, ErrMsg arg8) {
if(arg7 == 0) {
}
}
}
下面是修改过的MainActivity代码:
[java] view plaincopy
public class MainActivity extends Activity {
public EditText qqnum = null;
public EditText passwd = null;
public TextView userage = null;
public TextView usernick = null;
private ProgressBar xh_ProgressBar;
Context mContext = this;
QQLogin qqLogin = null;
Handler myHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
//login success
case 1:
{
String age = "年龄:" + qqLogin.getUserAge();
String nick = "昵称:"+qqLogin.getUserNickname();
userage.setText(age);
usernick.setText(nick);
xh_ProgressBar.setVisibility(View.INVISIBLE);
break;
}
//login error
case 2:
{
String age = "登录错误";
userage.setText(age);
usernick.setText("");
xh_ProgressBar.setVisibility(View.INVISIBLE);
break;
}
}
super.handleMessage(msg);
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
qqnum = (EditText)findViewById(R.id.qqnum);
passwd = (EditText)findViewById(R.id.passwd);
userage = (TextView)findViewById(R.id.age);
usernick = (TextView)findViewById(R.id.nick);
Button btnTest = (Button) findViewById(R.id.login);
xh_ProgressBar = (ProgressBar) findViewById(R.id.ProgressBar01);
btnTest.setOnClickListener(new Button.OnClickListener() { // 注1
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
String myqqnum = qqnum.getText().toString();
String mypassword = passwd.getText().toString();
Log.v(myqqnum,mypassword);
qqLogin = new QQLogin(mContext,myHandler);
qqLogin.SetLogin(myqqnum,mypassword);
xh_ProgressBar.setVisibility(View.VISIBLE);
}
});
}
}
总结:对于混淆过且难以还原出源码的包,可以直接整个嫁接过来使用,其它APK也同样适用。
demo工程源代码下载
本文出自 “执空法坛” 博客,请务必保留此出处http://chenjava.blog.51cto.com/374566/1571392
Android端不通过登录SDK实现QQ登录验证