三方登录
https://github.com/sunxlfree/ThirdpartyLoginAndShare
0 很重要的大前提
测试时key须为发布到平台的包的key
三方登录原理是根据平台记录的APP_ID或key等内容调用平台接口获取用户数据,而apk的生成都必须有key,所以如果测试时打包用的key不是上传到平台的key,微博会c8998,微信直接无法回调,QQ让你下载最新版本
android {
defaultConfig {
signingConfig signingConfigs.debug
}
buildTypes {
debug {
minifyEnabled false
signingConfig signingConfigs.debug
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
release {
minifyEnabled false
signingConfig signingConfigs.debug
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
signingConfigs {
debug {
storeFile file('C:/Users/Administrator/Desktop/XXXX')//你的keystore路径
storePassword "你的keystore密码"
keyAlias "你的key的文件名"
keyPassword "你的key的密码"
}
}
//为了能让自己在项目中看到libs,建议加上这段
sourceSets {
main() {
jniLibs.srcDirs = ['libs']
}
}
}
流程:根据平台注册的APP_KEY发起sso认证,获取到token,再用token和key获取到用户信息
微博开放平台获取到APP_KEY
下载weibosdk.jar(为了调用openapi接口),和weiboSDKCore_3.1.4.jar和相关so文件
-
布局页面
android:name= "com.sina.weibo.sdk.component.WeiboSdkBrowser"
android:configChanges= "keyboardHidden|orientation"
android:exported= "false"
android:windowSoftInputMode="adjustResize" >
代码
4.1 创建constant记录key Id
public class Constants {
public static final String WEIBO_APP_KEY = "微博的appKey";
public static final String WEIBO_REDIRECT_URL = "微博的授权回调页,须与官网一致,建议填写http://sns.whalecloud.com/sina2/callback";
public static final String WEIBO_SCOPE = "email,direct_messages_read,direct_messages_write,"
+ "friendships_groups_read,friendships_groups_write,statuses_to_me_read,"
+ "follow_app_official_microblog," + "invitation_write";
public static final String WX_APP_KEY = "微信的APPKEY";
public static final String QQ_APP_ID= "QQ的APPID";
}
4.2 string中添加
Token:%1$s \n有效期:%2$s
Token 仍在有效期内,无需再次登录。
4.3 页面代码
/**
* Created by Administrator on 2016/10/27.
*/
public class WeiboLoginActivity extends Activity implements OnClickListener {
private AuthInfo mWeiboAuthInfo;
private Button btnweibo;
private Button btnlogout;
private TextView tv;
private String nickname="";
/** 封装了 "access_token","expires_in","refresh_token",并提供了他们的管理功能 */
private Oauth2AccessToken mWeiboAccessToken;
/** 注意:SsoHandler 仅当 SDK 支持 SSO 时有效 */
private SsoHandler mWeiboSsoHandler;
/** 用户信息接口 */
private UsersAPI mWeiboUserAPI;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_weibologin);
initweibologin();
initViews();
initEvents();
initData();
}
private void initData() {
// 从 SharedPreferences 中读取上次已保存好 AccessToken 等信息,
// 第一次启动本应用,AccessToken 不可用
mWeiboAccessToken = WeiboAccessTokenKeeper.readAccessToken(this);
if (mWeiboAccessToken.isSessionValid()) {
updateWeiboTokenView(true);
}
}
private void initViews() {
btnweibo = (Button) findViewById(R.id.btn_weibo_login);
btnlogout = (Button) findViewById(R.id.btnlogout);
tv = (TextView) findViewById(R.id.content);
// 获取 Token View,并让提示 View 的内容可滚动(小屏幕可能显示不全)
tv.setMovementMethod(new ScrollingMovementMethod());
}
private void initEvents() {
btnweibo.setOnClickListener(this);
btnlogout.setOnClickListener(this);
}
/**
* 进行微博授权初始化操作
*/
private void initweibologin() {
// 初始化授权类对象,将应用的信息保存
mWeiboAuthInfo = new AuthInfo(this, ThirdpartyConstants.WEIBO_APP_KEY,
ThirdpartyConstants.WEIBO_REDIRECT_URL, ThirdpartyConstants.WEIBO_SCOPE);
mWeiboSsoHandler = new SsoHandler(WeiboLoginActivity.this, mWeiboAuthInfo);
}
/**
* 当 SSO 授权 Activity 退出时,该函数被调用。
*
* @see {@link Activity#onActivityResult}
*/
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
// SSO 授权回调
// 重要:发起 SSO 登陆的 Activity 必须重写 onActivityResults
if (mWeiboSsoHandler != null) {
mWeiboSsoHandler.authorizeCallBack(requestCode, resultCode, data);
}
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btn_weibo_login:// SSO 授权, ALL IN ONE
// 如果手机安装了微博客户端则使用客户端授权,没有则进行网页授权
mWeiboSsoHandler.authorize(new AuthListener());
break;
case R.id.btnlogout:// 用户登出
new LogoutAPI(WeiboLoginActivity.this, ThirdpartyConstants.WEIBO_APP_KEY,
WeiboAccessTokenKeeper.readAccessToken(WeiboLoginActivity.this)).logout(new WeiboLogOutRequestListener());
break;
default:
break;
}
}
/**
* 微博认证授权回调类。 1. SSO 授权时,需要在 {@link #onActivityResult} 中调用
* {@link SsoHandler#authorizeCallBack} 后, 该回调才会被执行。 2. 非 SSO
* 授权时,当授权结束后,该回调就会被执行。 当授权成功后,请保存该 access_token、expires_in、uid 等信息到
* SharedPreferences 中。
*/
class AuthListener implements WeiboAuthListener {
@Override
public void onCancel() {
Toast.makeText(WeiboLoginActivity.this, "取消授权", Toast.LENGTH_LONG)
.show();
}
@Override
public void onComplete(Bundle values) {
// 从 Bundle 中解析 Token
mWeiboAccessToken = Oauth2AccessToken.parseAccessToken(values);
if (mWeiboAccessToken.isSessionValid()) {
nickname = "用户名:"
+ String.valueOf(values
.get("com.sina.weibo.intent.extra.NICK_NAME"));
// 显示 Token
getWeiBoUserInfo();
updateWeiboTokenView(false);
// 保存 Token 到 SharedPreferences
WeiboAccessTokenKeeper.writeAccessToken(WeiboLoginActivity.this,
mWeiboAccessToken);
Toast.makeText(WeiboLoginActivity.this, "授权成功", Toast.LENGTH_SHORT)
.show();
// Toast.makeText(
// WeiboLoginActivity.this,
// "头像地址:"
// + String.valueOf(values
// .get("com.sina.weibo.intent.extra.USER_ICON")),
// Toast.LENGTH_LONG).show();
Toast.makeText(WeiboLoginActivity.this, nickname, Toast.LENGTH_LONG)
.show();
} else {
// 以下几种情况,您会收到 Code:
// 1. 当您未在平台上注册的应用程序的包名与签名时;
// 2. 当您注册的应用程序包名与签名不正确时;
// 3. 当您在平台上注册的包名和签名与您当前测试的应用的包名和签名不匹配时。
String code = values.getString("code");
String message = "授权失败";
if (!TextUtils.isEmpty(code)) {
message = message + "\nObtained the code: " + code;
}
Toast.makeText(WeiboLoginActivity.this, message, Toast.LENGTH_LONG)
.show();
}
}
private void getWeiBoUserInfo() {
new Thread(new Runnable() {
@Override
public void run() {
//获取用户信息的API
mWeiboUserAPI = new UsersAPI(WeiboLoginActivity.this, ThirdpartyConstants.WEIBO_APP_KEY, mWeiboAccessToken);
long uid = Long.parseLong(mWeiboAccessToken.getUid());
mWeiboUserAPI.show(uid, mListener);
}
}).start();
}
@Override
public void onWeiboException(WeiboException e) {
Toast.makeText(WeiboLoginActivity.this,
"Auth exception : " + e.getMessage(), Toast.LENGTH_LONG)
.show();
}
}
/**
* 显示当前 Token 信息。
*
* @param hasExisted
* 配置文件中是否已存在 token 信息并且合法
*/
private void updateWeiboTokenView(boolean hasExisted) {
String date = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss")
.format(new java.util.Date(mWeiboAccessToken.getExpiresTime()));
String format = getString(R.string.weibosdk_demo_token_to_string_format_1);
tv.setText(String.format(format, mWeiboAccessToken.getToken(), date));
String message = String.format(format, mWeiboAccessToken.getToken(), date);
if (hasExisted) {
message = getString(R.string.weibosdk_demo_token_has_existed)
+ "\n" + message;
}
message += "\n" + nickname;
tv.setText(message);
}
/**
* 微博 OpenAPI 回调接口。
*/
private RequestListener mListener = new RequestListener() {
@Override
public void onComplete(String response) {
if (!TextUtils.isEmpty(response)) {
LogUtil.i(TAG, response);
// 调用 User#parse 将JSON串解析成User对象
User user = User.parse(response);
if (user != null) {
Toast.makeText(WeiboLoginActivity.this,
"获取User信息成功,用户昵称:" + user.screen_name
+ "用户头像:" + user.avatar_hd
+ "用户性别" + user.gender,
Toast.LENGTH_LONG).show();
} else {
Toast.makeText(WeiboLoginActivity.this, response, Toast.LENGTH_LONG).show();
}
}
}
@Override
public void onWeiboException(WeiboException e) {
LogUtil.e(TAG, e.getMessage());
ErrorInfo info = ErrorInfo.parse(e.getMessage());
Toast.makeText(WeiboLoginActivity.this, info.toString(), Toast.LENGTH_LONG).show();
}
};
/**
* 登出按钮的监听器,接收登出处理结果。(API 请求结果的监听器)
* 看需求,微博自带,登录过的账号直接绕过确定,无法登录时传参设置
*/
private class WeiboLogOutRequestListener implements RequestListener {
@Override
public void onComplete(String response) {
if (!TextUtils.isEmpty(response)) {
try {
JSONObject obj = new JSONObject(response);
String value = obj.getString("result");
if ("true".equalsIgnoreCase(value)) {
WeiboAccessTokenKeeper.clear(WeiboLoginActivity.this);
Log.i("weibologout","weibo登出成功");
}
} catch (JSONException e) {
e.printStackTrace();
}
}
}
@Override
public void onWeiboException(WeiboException e) {
Log.i("weibologout","weibo登出异常");
}
}
}
微博bug
- 文件不存在C8998
-
考虑运行的keystore的md5是否与微博平台填写的签名一致
-
mAuthInfo = new AuthInfo()里设置的REDIRECT_URL是否与微博平台填写的授权回调页一致
-
客户端发起,打开WXEntryActivity中获得回调code,code拿到token和openID,再去请求用户信息接口,注意平台签名为keystore的md5
- 微信平台拿到AppID,导入libammsdk.jar
-
配置xml
在application设添加全局mIWxAPI
-
发起Req请求
public static IWXAPI mIWxAPI; MyApplication.mIWxAPI = WXAPIFactory.createWXAPI(LoginActivity.this, Constants.WX_APP_KEY, true); MyApplication.mIWxAPI.registerApp(Constants.WX_APP_KEY); SendAuth.Req req = new SendAuth.Req(); req.scope = "snsapi_userinfo"; req.state = "wechat_sdk_demo_test"; MyApplication.mIWxAPI.sendReq(req);//执行完毕这句话之后,会在WXEntryActivity回调
-
WXEntry回调类,切记回调后加入关闭WXEntryActivity,否则会有一层透明的activity覆盖在发起页上
public class WXEntryActivity extends Activity implements IWXAPIEventHandler { private Bundle bundle; public IWXAPI mIWxAPI; //这个实体类是我自定义的实体类,用来保存第三方的数据的实体类 private ThirdUserInfo info= null; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mIWxAPI = WXAPIFactory.createWXAPI(WXEntryActivity.this, ThirdpartyConstants.WX_APP_KEY, true); mIWxAPI.handleIntent(getIntent(), WXEntryActivity.this); //必须调用此句话 } @Override protected void onNewIntent(Intent intent) { super.onNewIntent(intent); mIWxAPI.handleIntent(intent, WXEntryActivity.this);//必须调用此句话 } @Override public void onReq(BaseReq req) { System. out.println(); } /** * Title: onResp * * API:https://open.weixin.qq.com/ cgi- bin/showdocument ?action=dir_list&t=resource/res_list&verify=1&id=open1419317853 &lang=zh_CN * Description:在此处得到Code之后调用https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code * 获取到token和openID。之后再调用https://api.weixin.qq.com/sns/userinfo?access_token=ACCESS_TOKEN&openid=OPENID 获取用户个人信息 * * @param arg0 */ @Override public void onResp(BaseResp arg0) { bundle=getIntent().getExtras(); SendAuth.Resp resp = new SendAuth.Resp( bundle); //获取到code之后,需要调用接口获取到access_token if (resp. errCode == BaseResp.ErrCode. ERR_OK) { String code = resp. code; try { getToken(code); } catch (IOException e) { e.printStackTrace(); } } else{ WXEntryActivity. this.finish(); } } //这个方法会取得accesstoken 和openID private void getToken(String code) throws IOException { // AnimationUtil.showLoadingDialog(this, "微信正在获取用户信息", false); String access_token_url = "https://api.weixin.qq.com/sns/oauth2/access_token" + "?appid=" + ThirdpartyConstants.WX_APP_KEY + "&secret=" + ThirdpartyConstants.WX_APP_SECRET + "&code=" + code + "&grant_type=authorization_code"; NetUtils.doGet(access_token_url, new NetUtils.HttpResponseCallBack() { @Override public void onSuccess(JSONObject response) { if (response == null || response.length() == 0) { Log.e("wechatGetToken", "null response"); return; } if(response.optString("access_token") == null || response.optString("access_token").length() == 0) { Log.e("wechatGetToken", "errcode=" + response.optString("errcode") + " errmsg=" + response.optString("errmsg")); return; } Map
data = new HashMap (); String[] keys = {"access_token", "expires_in", "refresh_token", "openid", "scope"}; for(int i=0; i data = new HashMap (); String[] keys = {"openid", "nickname", "sex", "province", "city", "country", "headimgurl", "unionid"}; for(int i=0; i
QQ登录
- app发布在腾讯开放平台后拿到APP_ID
-
manifest配置
android:name= "com.tencent.tauth.AuthActivity"
android:launchMode= "singleTask"
android:noHistory= "true" >
-
代码
/** * Created by Administrator on 2016/11/17. */ /** * Created by Administrator on 2016/11/4. */ public class QQLoginActivity extends Activity{ //初始化Tencent public Tencent mTencent = null; private ThirdUserInfo thirdUser = null; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_qqlogin); mTencent = Tencent.createInstance(ThirdpartyConstants.QQ_APP_ID, this); } public void qqLogin(View view) { mTencent.login(QQLoginActivity.this, "all", mIUiListener); } public void qqLogout(View view) { } /** * QQ的三方授权回调监听 */ IUiListener mIUiListener = new IUiListener() { @Override public void onCancel() { Log.i("qqauth","==cancel"); } @Override public void onComplete(Object arg0) { //登陆成功的回调,在此处可以获取用户信息 // AnimationUtil.showLoadingDialog(QQLoginActivity.this, "QQ登陆正在获取用户信息", false); initOpenidAndToken((JSONObject) arg0); updateUserInfo(); } @Override public void onError(UiError arg0) { Log.i("qqauth","==error"); } }; /** * QQ初始化OPENID以及TOKEN身份验证。 */ private void initOpenidAndToken(JSONObject jsonObject) { thirdUser = new ThirdUserInfo(); try { //这里的Constants类,是 com.tencent.connect.common.Constants类,下面的几个参数也是固定的 String token = jsonObject.getString(com.tencent.connect.common.Constants.PARAM_ACCESS_TOKEN); String expires = jsonObject.getString(com.tencent.connect.common.Constants.PARAM_EXPIRES_IN); //OPENID,作为唯一身份标识 String openId = jsonObject.getString(com.tencent.connect.common.Constants.PARAM_OPEN_ID); if (!TextUtils.isEmpty(token) && !TextUtils.isEmpty(expires) && !TextUtils.isEmpty(openId)) { //设置身份的token mTencent.setAccessToken(token, expires); mTencent.setOpenId(openId); thirdUser.setThirdID(openId); } } catch (Exception e) { } } /** * QQ在回调里面可以获取用户信息数据了 */ private void updateUserInfo() { if (mTencent != null && mTencent.isSessionValid()) { IUiListener listener = new IUiListener() { @Override public void onError(UiError e) { // AnimationUtil.cancelDialog(); } // 用户的信息回调在此处 @Override public void onComplete(final Object response) { // 返回Bitmap对象。 try { JSONObject obj = new JSONObject(response.toString()); thirdUser.setNickName(obj.optString("nickname")); thirdUser.setHeadimgurl(obj.optString("figureurl_qq_2")); thirdUser.setGender("男".equals(obj.optString("gender")) ? "1" : "0"); Log.i("qqNickname", thirdUser.getNickName()); Log.i("qqHeadImg", thirdUser.getHeadimgurl()); Log.i("qqGender", thirdUser.getGender()); } catch (JSONException e) { e.printStackTrace(); } } @Override public void onCancel() { // AnimationUtil.cancelDialog(); } }; UserInfo mInfo = new com.tencent.connect.UserInfo(QQLoginActivity.this, mTencent.getQQToken()); mInfo.getUserInfo(listener); } } /** * QQ的授权回调 */ @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { Log.d("TAG", "-->onActivityResult " + requestCode + " resultCode=" + resultCode); if (requestCode == com.tencent.connect.common.Constants.REQUEST_LOGIN || requestCode == com.tencent.connect.common.Constants.REQUEST_APPBAR) { Tencent.onActivityResultData(requestCode, resultCode, data, mIUiListener); } } }