微信第三方登录Android实现代码

作者:just_young 时间:2023-07-27 08:05:49 

记录一下微信第三方实现登录的方法。还是比较简单。

一、必要的准备工作

1.首先需要注册并被审核通过的微信开放平台帐号,然后创建一个移动应用,也需要被审核;

2.然后到资源中心下载开发微信所需的工具;

下载的网址:点击打开链接,有一个是SDK,一个是签名生成工具还有一个范例代码。

3.将SDK文件夹lib下的jar文件libammsdk.jar导入到项目工程中;

4.你的测试手机需要装好微信客户端;

5.在项目的AndroidManifest.xml文件中添加如下的权限:


<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.因为微信登录后会返回结果到我们自己的应用,因此,我们需要按如下的规则来建立一个可供回调的Activity

a. 在包名(申请移动应用时所填的包名)下新建一个名为wxapi的包,然后再在wxapi的包中新增一个WXEntryActivity类,这个类需要继承自Activity。

然后再在这个AndroidManifest.xml文件中,将这个activity的export属性设置为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. 实现IWXAPIEventHandler接口,微信发送的请求将回调到onReq方法,发送到微信请求的响应结果将回调到onResp方法

c. 在WXEntryActivity中将接收到的intent及实现了IWXAPIEventHandler接口的对象传递给IWXAPI接口的handleIntent方法,如下所示


api.handleIntent(getIntent(), this);

7.微信认证的时序图

这里有一点要注意,就是从上往下数第6个箭头,即通过code加上appid和appsecret换取access_token,其实这一步是在第三方应用服务器上做的,因为appsecret和access_token直接存储于客户端是非常不安全的。Android客户端获取code后,把这个code提交给应用服务器,应用服务器上保存有appsecret信息,由应用服务器来获取access_token,并用access_token来完成其它工作。微信第三方登录Android实现代码

二、Android代码

在上一步添加的WXEntryActivity对应的类文件中添加必要的代码,我的代码如下:


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();
     }
   }
 }
}

代码中的如下片段是用来拉起微信认证界面的。这里我使用了uuid来作为state参数,(该参数可用于防止csrf攻击(跨站请求伪造攻击),建议第三方带上该参数,可设置为简单的随机数加session进行校验)。


uuid = UUID.randomUUID().toString();
final SendAuth.Req req = new SendAuth.Req();
req.scope = "snsapi_userinfo";
req.state = uuid;
api.sendReq(req);

在用户接受认证后,微信应用会回调IWXAPIEventHandler接口的onResp方法。在该方法中,首先判断返回的resp的状态,若是正常状态,则判断state,然后从再从resp中获取code值。至此客户端便完成了它的工作。

因为客户端保留appsecret和access_token是非常不安全的,因此剩余信息的获取应放到我们的应用服务器上进行。

三、应用服务器代码

在Anroid客户端获取到code后,可提交到我们自己的应用服务器,在我们的应用服务器再通过code,来获取access_token,openid等用户信息。

1.通过code获取access_token,openid的方法是使用GET请求,按以下方式请求微信接口:

https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code;

2.通过access_token获取用户的一些信息的方式是通过GET请求使用微信的接口:

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中就是我们通过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;

/**
  * 用于公众号
  *
  * @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();
   }
 }

/**
  * 获得用户的元数据信息,只包括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) {
       }
       // 通过反射循环赋值
       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();
     }
   }
 }

}
标签:android,第三方,登录
0
投稿

猜你喜欢

  • Android使用FontMetrics对象计算位置坐标

    2023-02-06 15:35:20
  • java mail使用qq邮箱发邮件的配置方法

    2023-07-02 07:58:56
  • C# byte转为有符号整数实例

    2022-01-02 13:28:01
  • java日期工具类实例分享

    2023-05-17 17:50:40
  • 搭建MyBatis-Plus框架并进行数据库增删改查功能

    2023-11-09 04:33:43
  • hadoop是什么语言

    2021-06-18 07:02:38
  • Springboot中如何使用Redisson实现分布式锁浅析

    2023-10-01 18:48:29
  • flutter中的布局和响应式app方法示例

    2023-02-19 19:14:52
  • C#判断字符串是否是数字(实例)

    2023-07-30 02:41:09
  • c# 递归访问文件夹(删掉歌词文件)

    2022-02-11 02:52:16
  • SpringBoot数据访问自定义使用Druid数据源的方法

    2023-01-10 16:29:38
  • android I/0流操作文件(文件存储)

    2021-11-13 19:42:37
  • 浅谈C#在网络波动时防重复提交的方法

    2022-07-23 22:37:01
  • android使用OkHttp实现下载的进度监听和断点续传

    2022-03-23 12:37:07
  • java实现二分法的完整代码

    2023-08-18 22:09:06
  • java开发之Jdbc分页源码详解

    2021-10-28 16:06:48
  • 深入理解Java显式锁的相关知识

    2022-06-16 09:59:41
  • Android实现下拉菜单Spinner效果

    2022-03-21 07:27:07
  • Android onbackpressed实现返回键的拦截和弹窗流程分析

    2021-09-11 09:07:22
  • Android之侧滑菜单DrawerLayout的使用介绍

    2023-02-02 23:43:33
  • asp之家 软件编程 m.aspxhome.com