三方登錄
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文件
-
布局頁面
<activity
android:name= "com.sina.weibo.sdk.component.WeiboSdkBrowser"
android:configChanges= "keyboardHidden|orientation"
android:exported= "false"
android:windowSoftInputMode="adjustResize" >
</activity ><service android:name= "com.sina.weibo.sdk.net.DownloadService" android:exported= "false" > </service >
代碼
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中添加
<string name="weibosdk_demo_token_to_string_format_1">Token:%1$s \n有效期:%2$s</string>
<string name="weibosdk_demo_token_has_existed">Token 仍在有效期內,無需再次登錄。</string>
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
<activity android:name=".wxapi.WXEntryActivity" android:configChanges="keyboardHidden|orientation|screenSize" android:exported="true" android:screenOrientation="portrait" android:theme="@android:style/Theme.Translucent.NoTitleBar" />
在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<String, String> data = new HashMap<String, String>(); String[] keys = {"access_token", "expires_in", "refresh_token", "openid", "scope"}; for(int i=0; i<keys.length; i++) { data.put(keys[i], response.optString(keys[i])); } getUserInfo(data.get("access_token"),data.get("openid")); } @Override public void onFailure() { Log.e("wechatGetToken","error net"); WXEntryActivity.this.finish(); } }); } //獲取到token和openID之后,調用此接口得到身份信息 private void getUserInfo(String token,String openId) { String getUserInfoUrl = "https://api.weixin.qq.com/sns/userinfo" + "?access_token=" + token + "&openid=" + openId; NetUtils.doGet(getUserInfoUrl, new NetUtils.HttpResponseCallBack() { @Override public void onSuccess(JSONObject response) { // AnimationUtil.cancelDialog(); if (response == null || response.length() == 0) { Log.e("wechatGetToken", "null response"); return; } if(response.optString("openid") == null || response.optString("openid").length() == 0) { Log.e("wechatGetToken", "errcode=" + response.optString("errcode") + " errmsg=" + response.optString("errmsg")); return; } Map<String, String> data = new HashMap<String, String>(); String[] keys = {"openid", "nickname", "sex", "province", "city", "country", "headimgurl", "unionid"}; for(int i=0; i<keys.length; i++) { data.put(keys[i], response.optString(keys[i])); } Log.i("wechatUserInfo",data.get("nickname")); WXEntryActivity.this.finish(); } @Override public void onFailure() { // AnimationUtil.cancelDialog(); Log.e("wechatGetToken","error net"); WXEntryActivity.this.finish(); } }); } }
QQ登錄
- app發布在騰訊開放平臺后拿到APP_ID
-
manifest配置
<activity
android:name= "com.tencent.tauth.AuthActivity"
android:launchMode= "singleTask"
android:noHistory= "true" >
<intent-filter >
<action android:name ="android.intent.action.VIEW" /><category android:name ="android.intent.category.DEFAULT" /> <category android:name= "android.intent.category.BROWSABLE" /> <data android:scheme ="你的APP_ID" /> </intent-filter > </activity > <activity android:name= "com.tencent.connect.common.AssistActivity" android:configChanges="orientation|keyboardHidden|screenSize" android:theme= "@android:style/Theme.Translucent.NoTitleBar" />
-
代碼
/** * 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); } } }