微信第三方登錄Android實(shí)現(xiàn)代碼
記錄一下微信第三方實(shí)現(xiàn)登錄的方法。還是比較簡(jiǎn)單。
一、必要的準(zhǔn)備工作
1.首先需要注冊(cè)并被審核通過(guò)的微信開(kāi)放平臺(tái)帳號(hào),然后創(chuàng)建一個(gè)移動(dòng)應(yīng)用,也需要被審核;
2.然后到資源中心下載開(kāi)發(fā)微信所需的工具;
下載的網(wǎng)址:點(diǎn)擊打開(kāi)鏈接,有一個(gè)是SDK,一個(gè)是簽名生成工具還有一個(gè)范例代碼。
3.將SDK文件夾lib下的jar文件libammsdk.jar導(dǎo)入到項(xiàng)目工程中;
4.你的測(cè)試手機(jī)需要裝好微信客戶端;
5.在項(xiàng)目的AndroidManifest.xml文件中添加如下的權(quán)限:
<uses-permission android:name="android.permission.INTERNET"/> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/> <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/> <uses-permission android:name="android.permission.READ_PHONE_STATE"/> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
6.因?yàn)槲⑿诺卿浐髸?huì)返回結(jié)果到我們自己的應(yīng)用,因此,我們需要按如下的規(guī)則來(lái)建立一個(gè)可供回調(diào)的Activity
a. 在包名(申請(qǐng)移動(dòng)應(yīng)用時(shí)所填的包名)下新建一個(gè)名為wxapi的包,然后再在wxapi的包中新增一個(gè)WXEntryActivity類,這個(gè)類需要繼承自Activity。
然后再在這個(gè)AndroidManifest.xml文件中,將這個(gè)activity的export屬性設(shè)置為true,如下所示。
<activity android:name=".wxapi.WXEntryActivity" android:label="@string/title_activity_wxlogin" android:launchMode="singleTop" android:exported="true"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity>
b. 實(shí)現(xiàn)IWXAPIEventHandler接口,微信發(fā)送的請(qǐng)求將回調(diào)到onReq方法,發(fā)送到微信請(qǐng)求的響應(yīng)結(jié)果將回調(diào)到onResp方法
c. 在WXEntryActivity中將接收到的intent及實(shí)現(xiàn)了IWXAPIEventHandler接口的對(duì)象傳遞給IWXAPI接口的handleIntent方法,如下所示
api.handleIntent(getIntent(), this);
7.微信認(rèn)證的時(shí)序圖
這里有一點(diǎn)要注意,就是從上往下數(shù)第6個(gè)箭頭,即通過(guò)code加上appid和appsecret換取access_token,其實(shí)這一步是在第三方應(yīng)用服務(wù)器上做的,因?yàn)閍ppsecret和access_token直接存儲(chǔ)于客戶端是非常不安全的。Android客戶端獲取code后,把這個(gè)code提交給應(yīng)用服務(wù)器,應(yīng)用服務(wù)器上保存有appsecret信息,由應(yīng)用服務(wù)器來(lái)獲取access_token,并用access_token來(lái)完成其它工作。
二、Android代碼
在上一步添加的WXEntryActivity對(duì)應(yīng)的類文件中添加必要的代碼,我的代碼如下:
package com.example.justyoung.logintest.wxapi; import android.content.Intent; import android.os.Bundle; import android.support.v7.app.ActionBarActivity; import android.view.View; import android.widget.Button; import android.widget.Toast; import com.example.justyoung.logintest.HttpsHelper; import com.example.justyoung.logintest.R; import com.example.justyoung.logintest.fileExplorer.WXConstant; import com.tencent.mm.sdk.modelbase.BaseReq; import com.tencent.mm.sdk.modelbase.BaseResp; import com.tencent.mm.sdk.modelmsg.SendAuth; import com.tencent.mm.sdk.openapi.IWXAPI; import com.tencent.mm.sdk.openapi.IWXAPIEventHandler; import com.tencent.mm.sdk.openapi.WXAPIFactory; import java.io.IOException; import java.security.KeyManagementException; import java.security.NoSuchAlgorithmException; import java.util.UUID; public class WXEntryActivity extends ActionBarActivity implements IWXAPIEventHandler{ private Button wxLogin; private IWXAPI api; private static String uuid; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_wxlogin); wxLogin = (Button) findViewById(R.id.wx_login_button); wxLogin.setOnClickListener(new WXLoginEvent()); api = WXAPIFactory.createWXAPI(this, WXConstant.APPID); api.registerApp(WXConstant.APPID); api.handleIntent(getIntent(), this); } @Override public void onReq(BaseReq baseReq) { } @Override public void onNewIntent(Intent intent) { super.onNewIntent(intent); setIntent(intent); api.handleIntent(intent, this); } @Override public void onResp(BaseResp resp) { String result; switch (resp.errCode) { case BaseResp.ErrCode.ERR_OK: result = "OK"; SendAuth.Resp regResp = (SendAuth.Resp)resp; if (!regResp.state.equals(uuid)) return; String code = regResp.code; new WXLoginThread("https://192.168.2.133:8443/CloudStorageServer/wechat/login?code=" + code).start(); break; case BaseResp.ErrCode.ERR_USER_CANCEL: result = "USER_CANCEL"; break; case BaseResp.ErrCode.ERR_AUTH_DENIED: result = "ERR_AUTH_DENIED"; break; default: result = "errcode_unknown"; break; } Toast.makeText(this, result, Toast.LENGTH_LONG).show(); } class WXLoginEvent implements View.OnClickListener { @Override public void onClick(View v) { uuid = UUID.randomUUID().toString(); final SendAuth.Req req = new SendAuth.Req(); req.scope = "snsapi_userinfo"; req.state = uuid; api.sendReq(req); } } private class WXLoginThread extends Thread { private String url; public WXLoginThread(String url) { this.url = url; } @Override public void run() { HttpsHelper httpsHelper = new HttpsHelper(); try { httpsHelper.prepareHttpsConnection(url); String response = httpsHelper.connect(); } catch (KeyManagementException e) { e.printStackTrace(); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } } }
代碼中的如下片段是用來(lái)拉起微信認(rèn)證界面的。這里我使用了uuid來(lái)作為state參數(shù),(該參數(shù)可用于防止csrf攻擊(跨站請(qǐng)求偽造攻擊),建議第三方帶上該參數(shù),可設(shè)置為簡(jiǎn)單的隨機(jī)數(shù)加session進(jìn)行校驗(yàn))。
uuid = UUID.randomUUID().toString(); final SendAuth.Req req = new SendAuth.Req(); req.scope = "snsapi_userinfo"; req.state = uuid; api.sendReq(req);
在用戶接受認(rèn)證后,微信應(yīng)用會(huì)回調(diào)IWXAPIEventHandler接口的onResp方法。在該方法中,首先判斷返回的resp的狀態(tài),若是正常狀態(tài),則判斷state,然后從再?gòu)膔esp中獲取code值。至此客戶端便完成了它的工作。
因?yàn)榭蛻舳吮A鬭ppsecret和access_token是非常不安全的,因此剩余信息的獲取應(yīng)放到我們的應(yīng)用服務(wù)器上進(jìn)行。
三、應(yīng)用服務(wù)器代碼
在Anroid客戶端獲取到code后,可提交到我們自己的應(yīng)用服務(wù)器,在我們的應(yīng)用服務(wù)器再通過(guò)code,來(lái)獲取access_token,openid等用戶信息。
1.通過(guò)code獲取access_token,openid的方法是使用GET請(qǐng)求,按以下方式請(qǐng)求微信接口:
2.通過(guò)access_token獲取用戶的一些信息的方式是通過(guò)GET請(qǐng)求使用微信的接口:
https://api.weixin.qq.com/sns/userinfo?access_token=ACCESS_TOKEN&openid=OPENID
下面貼一下我自己使用的代碼:
private void handle(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String code = getParameter(request, "code"); if (isArgumentNullOrEmpty(code)) { Log.logger.info("code為空"); return; } Log.logger.info("收到code: " + code); try { AccessToken accessToken = new AccessToken("/sns/oauth2/access_token", "authorization_code", code); AccessToken.UserData userData = accessToken.getMetaData().getUserInfo(); ... // userData中就是我們通過(guò)access_token獲取的用戶信息了。 } catch (WeiXinException e) { Log.logException(e); writeMessage(response, e.getMessage()); return; } catch (Exception e) { Log.logException(e); writeMessage(response, "login error"); return; } }
package com.cyber_space.thirdparty.weixin; import java.io.IOException; import java.lang.reflect.Field; import java.net.URI; import java.net.URISyntaxException; import org.apache.http.HttpEntity; import org.apache.http.client.ClientProtocolException; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.utils.URIBuilder; import org.apache.http.entity.BufferedHttpEntity; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; import org.apache.http.util.EntityUtils; import com.cyber_space.util.JsonUtil; public class AccessToken { CloseableHttpClient httpClient; HttpGet httpGet; URI uri; String code; /** * 用于公眾號(hào) * * @throws URISyntaxException */ public AccessToken() throws URISyntaxException { uri = new URIBuilder().setScheme("https").setHost("api.weixin.qq.com").setPath("/cgi-bin/token") .setParameter("grant_type", "client_credential").setParameter("appid", WeiXinConfig.APP_ID) .setParameter("secret", WeiXinConfig.APP_SECRET).build(); httpClient = HttpClients.createDefault(); httpGet = new HttpGet(uri); } public AccessToken(String path, String grantType, String code) throws URISyntaxException { uri = new URIBuilder().setScheme("https").setHost("api.weixin.qq.com").setPath(path) .setParameter("grant_type", grantType).setParameter("appid", WeiXinConfig.APP_ID) .setParameter("secret", WeiXinConfig.APP_SECRET).setParameter("code", code).build(); httpClient = HttpClients.createDefault(); httpGet = new HttpGet(uri); } public String getAccessToken() throws ClientProtocolException, IOException { CloseableHttpResponse response = null; try { response = httpClient.execute(httpGet); HttpEntity httpEntity = response.getEntity(); if (httpEntity == null) return null; httpEntity = new BufferedHttpEntity(httpEntity); String returnString = EntityUtils.toString(httpEntity); String accessToken = com.cyber_space.util.JsonUtil.getAttribute(returnString, "access_token"); return accessToken; } finally { response.close(); } } /** * 獲得用戶的元數(shù)據(jù)信息,只包括openid和access_token * * @return * @throws ClientProtocolException * @throws IOException * @throws WeiXinException */ public UserData getMetaData() throws ClientProtocolException, IOException, WeiXinException { CloseableHttpResponse response = null; try { response = httpClient.execute(httpGet); HttpEntity httpEntity = response.getEntity(); if (httpEntity == null) return null; httpEntity = new BufferedHttpEntity(httpEntity); String returnString = EntityUtils.toString(httpEntity); JsonUtil jUtil = new JsonUtil(returnString, JsonUtil.JSONOBJECT); String error = null; try { error = jUtil.getAttribute("errcode"); } catch (Exception e) { } if (error != null && !error.equals("")) { throw new WeiXinException(WeiXinException.INVALID_OPENID); } String openid = jUtil.getAttribute("openid"); String accessToken = jUtil.getAttribute("access_token"); UserData uData = new UserData(openid, accessToken); return uData; } finally { response.close(); } } public class UserData { public String openid; public String accessToken; public String nickname; public String sex; public String province; public String city; public String country; public String headimgurl; public String privilege; public String unionid; public UserData(String openid, String accessToken) { this.openid = openid; this.accessToken = accessToken; } public UserData getUserInfo() throws IOException, IllegalArgumentException, IllegalAccessException, URISyntaxException, WeiXinException { URI uri = new URIBuilder().setScheme("https").setHost("api.weixin.qq.com").setPath("/sns/userinfo") .setParameter("access_token", this.accessToken).setParameter("openid", this.openid).build(); HttpGet httpGet = new HttpGet(uri); CloseableHttpResponse response = null; try { response = httpClient.execute(httpGet); HttpEntity httpEntity = response.getEntity(); if (httpEntity == null) throw null; httpEntity = new BufferedHttpEntity(httpEntity); String jsonString = EntityUtils.toString(httpEntity); JsonUtil jUtil = new JsonUtil(jsonString, JsonUtil.JSONOBJECT); String errcode = null; try { errcode = jUtil.getAttribute("errcode"); } catch (Exception e) { } // 通過(guò)反射循環(huán)賦值 if (errcode == null || errcode.equals("")) { for (Field i : getClass().getFields()) { if (!i.getName().equals("accessToken")) i.set(this, jUtil.getAttribute(i.getName())); } return this; } else { throw new WeiXinException(WeiXinException.INVALID_ACCESSTOKEN); } } finally { response.close(); } } } }
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- Android第三方登錄之QQ登錄
- Android第三方登錄之騰訊QQ登錄的實(shí)例代碼
- Android實(shí)現(xiàn)第三方登錄的上拉展開(kāi),下拉隱藏,下拉隱藏示例
- Android微信第三方登錄(個(gè)人筆記)
- Android 第三方登錄、分享(ShareSDK、友盟)
- Android集成新浪微博第三方登錄的方法
- Android實(shí)現(xiàn)第三方授權(quán)登錄、分享以及獲取用戶資料
- Android調(diào)用第三方QQ登錄代碼分享
- React Native第三方平臺(tái)分享的實(shí)例(Android,IOS雙平臺(tái))
- Android實(shí)現(xiàn)QQ的第三方登錄和分享
相關(guān)文章
Android實(shí)現(xiàn)簡(jiǎn)單畫圖畫板
這篇文章主要為大家詳細(xì)介紹了Android實(shí)現(xiàn)簡(jiǎn)單畫圖畫板,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-01-01Android實(shí)現(xiàn)左右滑動(dòng)切換圖片
這篇文章主要為大家詳細(xì)介紹了Android實(shí)現(xiàn)左右滑動(dòng)切換圖片,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-05-05ViewPager+PagerAdapter實(shí)現(xiàn)帶指示器的引導(dǎo)頁(yè)
這篇文章主要為大家詳細(xì)介紹了ViewPager+PagerAdapter實(shí)現(xiàn)帶指示器的引導(dǎo)頁(yè),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-09-09Android Studio導(dǎo)入jar包過(guò)程詳解
這篇文章主要介紹了Android Studio導(dǎo)入jar包過(guò)程詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-11-11android自定義view實(shí)現(xiàn)鐘表效果
這篇文章主要為大家詳細(xì)介紹了android自定義view實(shí)現(xiàn)鐘表效果,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-12-12android 震動(dòng)和提示音的實(shí)現(xiàn)代碼
這篇文章主要介紹了android 震動(dòng)和提示音的實(shí)現(xiàn)代碼,代碼簡(jiǎn)單易懂,非常不錯(cuò),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2019-12-12Android 自定View實(shí)現(xiàn)仿QQ運(yùn)動(dòng)步數(shù)圓弧及動(dòng)畫效果
這篇文章主要介紹了Android自定義view實(shí)現(xiàn)高仿QQ運(yùn)動(dòng)步數(shù)圓弧及動(dòng)畫效果的實(shí)例代碼,本文涉及到繪制圓弧需要具備的知識(shí)點(diǎn),需要的朋友可以參考下2016-10-10