安卓集成微信登陸

一、首先在Application的onCreate中寫:

?
1
2
3
4
5
6
7
// GeneralAppliction.java
public static IWXAPI sApi;
@Override
public void onCreate() {
  super .onCreate();
  sApi = WXEntryActivity.initWeiXin( this , AppConst.WEIXIN_APP_ID);
}

二、在需要登錄的地方添加:

?
1
2
// MainActivity.java
WXEntryActivity.loginWeixin(MainActivity. this , GeneralAppliction.sApi);

三、下面對具體的集成步驟做詳細的描述。

集成步驟:

     1、在開放平臺註冊創建應用,申請登錄權限

     2、下載sdk,拷貝相關文件到項目工程目錄

     3、全局初始化微信組件

     4、請求授權登錄,獲取code

     5、通過code獲取授權口令access_token

     6、在第5步判斷access_token是否存在和過期

     7、如果access_token過期無效,就用refresh_token來刷新

     8、使用access_token獲取用戶信息

1. 在開放平臺註冊創建應用,申請登錄權限

這一步其實不用怎麼講,無法就是在微信開放平臺上註冊一個賬號,然後創建移動應用。

需要注意的是:應用簽名的部分

此處應用簽名我使用的是線上的key的md5,關於這個需要注意的問題可以看:Android的簽名總結

2. 下載sdk,拷貝相關文件到項目工程目錄

開發工具包(SDK)的下載:可以使用微信分享、登錄、收藏、支付等功能需要的庫以及文件

示例Demo

下載後把libammsdk.jar文件拷貝到AS工程的libs目錄,並把示例Demo裏源文件目錄下的wxapi目錄整個拷貝到,工程目錄的src下的根包下:

如果wxapi這個文件夾放的位置不對,講無法登錄,微信sdk無法找到登錄的Activity授權功能。然後在Manifest.xml裏面加入:

?
1
2
3
4
5
6
<activity
  android:name= ".wxapi.WXEntryActivity"
  android:theme= "@android:style/Theme.Translucent.NoTitleBar"
  android:configChanges= "keyboardHidden|orientation|screenSize"
  android:exported= "true"
  android:screenOrientation= "portrait" />

3. 全局初始化微信組件

全局初始化微信組件,當然是Application的onCreate裏(當然Activity的onCreate也是可以的,爲了全局使用微信api對象方便操作):

?
1
2
3
4
5
6
7
8
9
10
11
@Override
public void onCreate() {
  super .onCreate();
  // 初始化微信組件
  initWeiXin();
}
 
public static IWXAPI sApi;
private void initWeiXin() {
  sApi = WXEntryActivity.initWeiXin( this , AppConst.WEIXIN_APP_ID);
}

4. 請求授權登錄,獲取code

爲了同一業務的單一原則我把微信相關的都統一封裝到了wxapi包下和WXEntryActivity中:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
// 實現IWXAPIEventHandler 接口,以便於微信事件處理的回調
public class WXEntryActivity extends Activity implements IWXAPIEventHandler {
 
  private static final String WEIXIN_ACCESS_TOKEN_KEY = "wx_access_token_key" ;
  private static final String WEIXIN_OPENID_KEY = "wx_openid_key" ;
  private static final String WEIXIN_REFRESH_TOKEN_KEY = "wx_refresh_token_key" ;
 
  private Gson mGson;
  @Override
  public void onCreate(Bundle savedInstanceState) {
  super .onCreate(savedInstanceState);
  // 微信事件回調接口註冊
  GeneralAppliction.sApi.handleIntent(getIntent(), this );
  mGson = new Gson();
  }
 
  /**
  * 微信組件註冊初始化
  * @param context 上下文
  * @param weixin_app_id appid
  * @return 微信組件api對象
  *
  /
  public static IWXAPI initWeiXin(Context context, @NonNull String weixin_app_id) {
  if (TextUtils.isEmpty(weixin_app_id)) {
  Toast.makeText(context.getApplicationContext(), "app_id 不能爲空", Toast.LENGTH_SHORT).show();
  }
  IWXAPI api = WXAPIFactory.createWXAPI(context, weixin_app_id, true);
  api.registerApp(weixin_app_id);
  return api;
  }
 
  /**
  * 登錄微信
  *
  * @param api 微信服務api
  */
  public static void loginWeixin(Context context, IWXAPI api) {
  // 判斷是否安裝了微信客戶端
  if (!api.isWXAppInstalled()) {
  Toast.makeText(context.getApplicationContext(), "您還未安裝微信客戶端!" , Toast.LENGTH_SHORT).show();
  return ;
  }
  // 發送授權登錄信息,來獲取code
  SendAuth.Req req = new SendAuth.Req();
  // 應用的作用域,獲取個人信息
  req.scope = "snsapi_userinfo" ;
  /**
  * 用於保持請求和回調的狀態,授權請求後原樣帶回給第三方
  * 爲了防止csrf攻擊(跨站請求僞造攻擊),後期改爲隨機數加session來校驗
  */
  req.state = "app_wechat" ;
  api.sendReq(req);
  }
 
  // 微信發送請求到第三方應用時,會回調到該方法
  @Override
  public void onReq(BaseReq req) {
  switch (req.getType()) {
  case ConstantsAPI.COMMAND_GETMESSAGE_FROM_WX:
  break ;
  case ConstantsAPI.COMMAND_SHOWMESSAGE_FROM_WX:
  break ;
  default :
  break ;
  }
  }
  // 第三方應用發送到微信的請求處理後的響應結果,會回調到該方法
  @Override
  public void onResp(BaseResp resp) {
  switch (resp.errCode) {
  // 發送成功
  case BaseResp.ErrCode.ERR_OK:
  // 獲取code
  String code = ((SendAuth.Resp) resp).code;
  // 通過code獲取授權口令access_token
  getAccessToken(code);
  break ;
  }
  }
}

小夥伴有疑問code是啥玩意:

第三方通過code進行獲取access_token的時候需要用到,code的超時時間爲10分鐘,一個code只能成功換取一次access_token即失效。code的臨時性和一次保障了微信授權登錄的安全性。第三方可通過使用https和state參數,進一步加強自身授權登錄的安全性。

這樣客戶端使用的地方只要:

?
1
WXEntryActivity.loginWeixin(MainActivity. this , GeneralAppliction.sApi);

5. 通過code獲取授權口令access_token

我們在onResp的回調方法中獲取了code,然後通過code獲取授權口令access_token:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
/**
* 獲取授權口令
*/
private void getAccessToken(String code) {
  "appid=" + AppConst.WEIXIN_APP_ID +
  "&secret=" + AppConst.WEIXIN_APP_SECRET +
  "&code=" + code +
  "&grant_type=authorization_code" ;
  // 網絡請求獲取access_token
  httpRequest(url, new ApiCallback<String>() {
  @Override
  public void onSuccess(String response) {
  Logger.e(response);
  // 判斷是否獲取成功,成功則去獲取用戶信息,否則提示失敗
  processGetAccessTokenResult(response);
  }
  @Override
  public void onError( int errorCode, final String errorMsg) {
  Logger.e(errorMsg);
  showMessage( "錯誤信息: " + errorMsg);
  }
  @Override
  public void onFailure(IOException e) {
  Logger.e(e.getMessage());
  showMessage( "登錄失敗" );
  }
  });
}
 
/**
* 處理獲取的授權信息結果
* @param response 授權信息結果
*/
private void processGetAccessTokenResult(String response) {
  // 驗證獲取授權口令返回的信息是否成功
  if (validateSuccess(response)) {
  // 使用Gson解析返回的授權口令信息
  WXAccessTokenInfo tokenInfo = mGson.fromJson(response, WXAccessTokenInfo. class );
  Logger.e(tokenInfo.toString());
  // 保存信息到手機本地
  saveAccessInfotoLocation(tokenInfo);
  // 獲取用戶信息
  getUserInfo(tokenInfo.getAccess_token(), tokenInfo.getOpenid());
  } else {
  // 授權口令獲取失敗,解析返回錯誤信息
  WXErrorInfo wxErrorInfo = mGson.fromJson(response, WXErrorInfo. class );
  Logger.e(wxErrorInfo.toString());
  // 提示錯誤信息
  showMessage( "錯誤信息: " + wxErrorInfo.getErrmsg());
  }
}
 
/**
* 驗證是否成功
*
* @param response 返回消息
* @return 是否成功
*/
private boolean validateSuccess(String response) {
  String errFlag = "errmsg" ;
  return (errFlag.contains(response) && ! "ok" .equals(response))
  || (! "errcode" .contains(response) && !errFlag.contains(response));
}

6. 在第5步判斷access_token是否存在和過期

在回調的onResp方法中獲取code後,處理access_token是否登錄過或者過期的問題:

?
1
2
3
4
5
6
7
8
9
10
// 從手機本地獲取存儲的授權口令信息,判斷是否存在access_token,不存在請求獲取,存在就判斷是否過期
String accessToken = (String) ShareUtils.getValue( this , WEIXIN_ACCESS_TOKEN_KEY, "none" );
String openid = (String) ShareUtils.getValue( this , WEIXIN_OPENID_KEY, "" );
if (! "none" .equals(accessToken)) {
  // 有access_token,判斷是否過期有效
  isExpireAccessToken(accessToken, openid);
} else {
  // 沒有access_token
  getAccessToken(code);
}

判斷授權口令是否有效:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
/**
* 判斷accesstoken是過期
* @param accessToken token
* @param openid 授權用戶唯一標識
*/
private void isExpireAccessToken( final String accessToken, final String openid) {
  "access_token=" + accessToken +
  "&openid=" + openid;
  httpRequest(url, new ApiCallback<String>() {
  @Override
  public void onSuccess(String response) {
  Logger.e(response);
  if (validateSuccess(response)) {
  // accessToken沒有過期,獲取用戶信息
  getUserInfo(accessToken, openid);
  } else {
  // 過期了,使用refresh_token來刷新accesstoken
  refreshAccessToken();
  }
  }
  @Override
  public void onError( int errorCode, final String errorMsg) {
  Logger.e(errorMsg);
  showMessage( "錯誤信息: " + errorMsg);
  }
  @Override
  public void onFailure(IOException e) {
  Logger.e(e.getMessage());
  showMessage( "登錄失敗" );
  }
  });
}

7. 如果access_token過期無效,就用refresh_token來刷新

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
/**
  * 刷新獲取新的access_token
  *
/
private void refreshAccessToken() {
  // 從本地獲取以存儲的refresh_token
  final String refreshToken = (String) ShareUtils.getValue( this , WEIXIN_REFRESH_TOKEN_KEY, "" );
  if (TextUtils.isEmpty(refreshToken)) {
  return ;
  }
  // 拼裝刷新access_token的url請求地址
  "appid=" + AppConst.WEIXIN_APP_ID +
  "&grant_type=refresh_token" +
  "&refresh_token=" + refreshToken;
  // 請求執行
  httpRequest(url, new ApiCallback<String>() {
  @Override
  public void onSuccess(String response) {
  Logger.e( "refreshAccessToken: " + response);
  // 判斷是否獲取成功,成功則去獲取用戶信息,否則提示失敗
  processGetAccessTokenResult(response);
  }
  @Override
  public void onError( int errorCode, final String errorMsg) {
  Logger.e(errorMsg);
  showMessage( "錯誤信息: " + errorMsg);
  // 重新請求授權
  loginWeixin(WXEntryActivity. this .getApplicationContext(), GeneralAppliction.sApi);
  }
  @Override
  public void onFailure(IOException e) {
  Logger.e(e.getMessage());
  showMessage( "登錄失敗" );
  // 重新請求授權
  loginWeixin(WXEntryActivity. this .getApplicationContext(), GeneralAppliction.sApi);
  }
  });
}

8. 使用access_token獲取用戶信息

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/**
  * 獲取用戶信息
  *
/
private void getUserInfo(String access_token, String openid) {
  "access_token=" + access_token +
  "&openid=" + openid;
  httpRequest(url, new ApiCallback<String>() {
  @Override
  public void onSuccess(String response) {
  // 解析獲取的用戶信息
  WXUserInfo userInfo = mGson.fromJson(response, WXUserInfo. class );
  Logger.e( "用戶信息獲取結果:" + userInfo.toString()); }
  @Override
  public void onError( int errorCode, String errorMsg) {
  showMessage( "錯誤信息: " + errorMsg);
  }
  @Override
  public void onFailure(IOException e) {
  showMessage( "獲取用戶信息失敗" );
  }
  });
}

通信部分

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
private OkHttpClient mHttpClient = new OkHttpClient.Builder().build();
private Handler mCallbackHandler = new Handler(Looper.getMainLooper());
/**
  * 通過Okhttp與微信通信
  * * @param url 請求地址
  * @throws Exception
  */
public void httpRequest(String url, final ApiCallback<String> callback) {
  Logger.e( "url: %s" , url);
  final Request request = new Request.Builder()
  .url(url)
  .get()
  .build();
  mHttpClient.newCall(request).enqueue( new Callback() {
  @Override
  public void onFailure(Call call, final IOException e) {
  if (callback != null ) {
  mCallbackHandler.post( new Runnable() {
   @Override
   public void run() {
   // 請求失敗,主線程回調
   callback.onFailure(e);
   }
  });
  }
  }
  @Override
  public void onResponse(Call call, final Response response) throws IOException {
  if (callback != null ) {
  if (!response.isSuccessful()) {
   mCallbackHandler.post( new Runnable() {
   @Override
   public void run() {
相關文章
相關標籤/搜索