微信第三方登录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代码
在上一步添加的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();
}
}
}
}


猜你喜欢
- 一.前言这一篇来看看 SpringIOC 里面的一个细节点 , 来简单看看 BeanDefinition 这个对象 , 以及有没有办法对其进
- 写在自定义之前我们也许会遇到,自定义控件的触屏事件处理,先来了解一下View类中的,onTouch事件和onTouchEvent事件。1、b
- 请求参数解析客户端请求在handlerMapping中找到对应handler后,将会继续执行DispatchServlet的doPatch(
- 昨天有个粉丝加了我,问我如何实现类似shiro的资源权限表达式的访问控制。我以前有一个小框架用的就是shiro,权限控制就用了资源权限表达式
- 本文实例讲述了Java基于swing实现的弹球游戏代码。分享给大家供大家参考。主要功能代码如下:package Game;import ja
- 本文实例讲述了.net文件上传时实现通过文件头确认文件类型的方法,其中 script 用来返回给页面的数据,读者还可以根据自身需要对相关部分
- 先上效果图文件和加密文件之间的转换。先添加辅助类public class AES_EnorDecrypt { &n
- 引言float和double类型的主要设计目标是为了科学计算和工程计算。他们执行二进制浮点运算,这是为了在广域数值范围上提供较为精确的快速近
- 用Linq从一个集合选取几列得到一个新的集合-可改列名
- 相信有些同学跟我一样,曾经对这个问题很疑惑。在网上也看了一些别人说的观点,评论不一。有说有值传递和引用传递两种,也有说只有值传递的,这里只说
- 一、概念 工厂方法模式是类的创建模式,又叫虚
- WebMvcConfigurer配置类其实是Spring内部的一种配置方式,采用JavaBean的形式来代替传统的xml配置文件形式进行针对
- 前言本文主要给大家介绍了关于spring mvc注解@ModelAttribute妙用的相关内容,分享出来供大家参考学习,下面话不多说了,来
- 目录一、泛型类型二、为什么需要泛型三、类型擦除四、类型擦除的后遗症五、Kotlin 泛型六、上界约束七、类型通配符 & 星号投影八、
- 在java开发中,类、接口、方法,都需要进行注释,注释内容如图:注释中的基本元素有:描述、作者、创建日期。可增加元素有:修改日期、修改内容、
- 目录配置创建OkHttpClient同步get请求异步get请求同步post请求异步post请求上传文件表单提交下面是官网给出的OKHTTP
- c++换行符有哪些\n 换行,光标移到下一行的开头;endl,把缓冲槽的内容输出到控制台;\r 回车,光标移到当前行的开头,不会换到下一行,
- 前言我们通常使用Spring boot做项目搭建的基础框架,必然少不了它的内置日志框架Logback,在spring-boot-starte
- 本文实例讲述了Android DigitalClock组件用法。分享给大家供大家参考,具体如下:DigitalClock组件的使用很简单,先
- 如果你想知道java annotation是什么?你可以先看看:“http://www.infoq.com/articles/Annotat