微信登录开发集成
前言
最近遇到了QQ和微信三方登录的需求,就试着接入了一下。QQ三方的登录还是挺简单的,demo和开发文档也挺完整,按照步骤来分分钟集成,调试也不麻烦。到了微信登录就比较坑爹了,文档不完整,demo也运行不起来,关键是调试必须是release包才能调试,坑爹的嘛。现在就我遇到的一些坑和开发心得在此记录一下,好久也没写博客了,主要是太忙(lan)了。
准备工作
开发的时候需要appid和secret这两个东西,所以需要去微信开放平台去注册一个开发者账号,再创建你的应用,最后审核通过了你的这两个值就能获取到了。具体步骤请移步这位大佬的博客,里面的东西算是很详细很详细了。需要注意的是,我们开发的包名和签名必须要和微信创建应用的包名和签名一致,不然是调用不起微信的,也不会报错,就很懵逼的。获取签名的工具:Signature。
正式开发
环境配置搭建
- 环境依赖集成
在工程项目的build.gradle文件中添加依赖。
//okhttp网络请求
api 'com.squareup.okhttp3:okhttp:4.2.2'
//微信登录SDK
api 'com.tencent.mm.opensdk:wechat-sdk-android-without-mta:5.3.1'
- 新建必须要的WXEntryActivity.java文件
在包名目录下新建wxapi文件夹,路径名字必须和java名字必须是这两个。如图所示:
- 注册WXEntryActivity
在工程中的AndroidManifest.xml文件,注册WXEntryActivity。
<activity android:name=".wxapi.WXEntryActivity"
></activity>
- 实现IWXAPIEventHandler接口
打开WXEntryActivity.java文件,实现IWXAPIEventHandler接口,并重写里面的两个方法onReq(BaseReq baseReq)和onResp(BaseResp baseResp)。注意:onCreate里面的setContentView()的方法要删除掉。
public class WXEntryActivity extends Activity implements IWXAPIEventHandler{
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
@Override
public void onReq(BaseReq baseReq) {
}
@Override
public void onResp(BaseResp baseResp) {
}
}
功能实现
打开MainActivity
在onCreate方法中向微信终端注册应用的 id。
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
api = WXAPIFactory.createWXAPI(this, Constants.WECHAT_ID, false);
api.registerApp(Constants.WECHAT_ID);
}
public class Constants {
public static final String WECHAT_ID = "你的appid";//微信
public static final String WEIXIN = "wechat";
public static final String APP_SECRET = "你的secret_key";
}
触发微信登录的请求按钮
微信登录请求方法
/**
* 微信登陆
*/
public void wxLogin() {
if (!api.isWXAppInstalled()) {
ToastUtils.showToast(mContext, "您的设备未安装微信客户端");
} else {
final SendAuth.Req req = new SendAuth.Req();
//应用授权作用域,如获取用户个人信息则填写 snsapi_userinfo
req.scope = "snsapi_userinfo";
//用于保持请求和回调的状态,授权请求后原样带回给第三方。
//该参数可用于防止 csrf 攻击(跨站请求伪造攻击),
//建议第三方带上该参数,可设置为简单的随机数加 session 进行校验
req.state = "wechat_sdk_demo_test";
api.sendReq(req);
}
}
请求方法调用了之后,如果appid这些东西都配置成功了的话,就会跳转到微信登录的授权界面。注意:有的时候会出现api.sendReq(req)的返回值为true但是微信登录授权界面没有被拉起的情况,就有可能是开发者平台的包名和签名和你现在的应用不一致所导致的,我当时就遇到过这个问题。
打开WXEntryActivity
获取wxapi
public class WXEntryActivity extends Activity implements IWXAPIEventHandler{
private IWXAPI iwxapi;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
iwxapi = WXAPIFactory.createWXAPI(this, Constants.WECHAT_ID, false);
try {
Intent intent = getIntent();
iwxapi.handleIntent(intent, this);
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
setIntent(intent);
iwxapi.handleIntent(intent, this);
}
@Override
public void onReq(BaseReq baseReq) {
}
@Override
public void onResp(BaseResp baseResp) {
}
拉起微信登录成功之后,回调的数据会在onResp(BaseResp baseResp)方法里面。
@Override
public void onResp(BaseResp baseResp) {
//登录回调
switch (baseResp.errCode){
case BaseResp.ErrCode.ERR_OK:
String code = ((SendAuth.Resp) baseResp).code;
//获取accesstoken
getAccessToken(code);
break;
//用户拒绝授权
case BaseResp.ErrCode.ERR_AUTH_DENIED:
finish();
break;
//用户取消授权
case BaseResp.ErrCode.ERR_USER_CANCEL:
finish();
break;
}
}
当baseResp.errCode的值为0的时候,即为表示登录成功,返回码链接.
然后我们需要一个获取到accessToken值,再去获取微信登录用户相关信息。根据微信开发平台给的链接以及相关字段,进行获取。
private void getAccessToken(String code) {
/**
* access_token:接口调用凭证
* appid:应用唯一标识,在微信开放平台提交应用审核通过后获得。
* secret:应用密钥AppSecret,在微信开放平台提交应用审核通过后获得。
* code:填写第一步获取的code参数。
* grant_type:填authorization_code。
*/
StringBuffer loginUrl = new StringBuffer();
loginUrl.append("https://api.weixin.qq.com/sns/oauth2/access_token")
.append("?appid=")
.append(Constants.WECHAT_ID)
.append("&secret=")
.append(Constants.APP_SECRET)
.append("&code=")
.append(code)
.append("&grant_type=authorization_code");
OkHttpClient okHttpClient = new OkHttpClient();
Request request = new Request.Builder()
.url(loginUrl.toString())
.get()
.build();
Call call = okHttpClient.newCall(request);
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
Log.e("onFailure1", call.toString());
}
@Override
public void onResponse(Call call, Response response) throws IOException {
String responseInfo = response.body().string();
String access = null;
String openid = null;
//用json去解析返回来的access和token值
try {
JSONObject jsonObject = new JSONObject(responseInfo);
access = jsonObject.getString("access_token");
openid = jsonObject.getString("openid");
} catch (JSONException e) {
e.printStackTrace();
}
getUserInfo(access, openid);
}
});
}
onResponse(Call call, Response response)回调方法中,response就是我们获取到的数据,然后再通过json解析返回来的access和token值。注意:
response.body().string()中的string()方法,只能被调用一次,如果连续被调用就会抛出异常:
java.lang.IllegalStateException: closed
有兴趣的同学可以去看一下string()方法的源码。
接下来,就通过getUserInfo(access, openid)方法去获取到微信登录的相关信息:
private void getUserInfo(String access,String openid){
String getUserInfoUrl = "https://api.weixin.qq.com/sns/userinfo?access_token=" + access + "&openid=" + openid;
OkHttpClient okHttpClient = new OkHttpClient();
Request request = new Request.Builder()
.url(getUserInfoUrl)
.get()
.build();
Call call = okHttpClient.newCall(request);
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
Log.e("onFailure2", call.toString());
}
@Override
public void onResponse(Call call, Response response) throws IOException {
String responseInfo = response.body().string();
Log.e("onResponse2", responseInfo);
//用SharedPreference来缓存字符串
SharedPreferences.Editor editor = getSharedPreferences("userInfo", MODE_PRIVATE).edit();
editor.putString("responseInfo",responseInfo);
editor.commit();
finish();
}
});
}
获取到用户信息之后,我们可以通过SharedPreference来缓存用户信息,然后在MainActivity中去获取这个用户信息。当然也可以用其他的方法来存储这个信息,这里用SharedPreference比较方便。
解析用户信息
在MainActivity的onResume()方法里面来获取到刚刚缓存的用户信息,Constants.WEIXIN.equals(type)里面的type值在这里是为了区分用户是点击了QQ登录还是微信登录。
@Override
protected void onResume() {
super.onResume();
if (Constants.WEIXIN.equals(type)){
//说明当前调用了微信登陆 那么我们就可以去拿信息
SharedPreferences sharedPreferences = getSharedPreferences("userInfo", MODE_PRIVATE);
String response = sharedPreferences.getString("responseInfo","");
if (!TextUtils.isEmpty(response)){
try {
JSONObject jsonObject = new JSONObject(response);
/**
* 这里可以返回如下数据
* openid 普通用户的标识,对当前开发者帐号唯一
* nickname 普通用户昵称
* sex 普通用户性别,1为男性,2为女性,0为没有设置性别
* province 普通用户个人资料填写的省份
* city 普通用户个人资料填写的城市
* country 国家,如中国为CN
* headimgurl 用户头像,最后一个数值代表正方形头像大小(有0、46、64、96、132数值可选,0代表640*640正方形头像),用户没有头像时该项为空
* privilege 用户特权信息,json数组,如微信沃卡用户为(chinaunicom)
* unionid 用户统一标识。针对一个微信开放平台帐号下的应用,同一用户的unionid是唯一的。
*/
//openid
openID = jsonObject.getString("openid");
//性别
int sexCode = jsonObject.getInt("sex");
if (sexCode == 2){
sex = "女";
}else {
sex = "男";
}
//头像
image = jsonObject.getString("headimgurl");
} catch (JSONException e) {
e.printStackTrace();
}
}
}
}
总结
到这里,微信登录的相关功能都基本上完成了。过程中我们还是会遇到各种奇奇怪怪的问题,比如说appid不正确、开放平台上面的签名和包名与我们调试的demo的签名包名不一致、应用审核未通过等等,这些都需要我们根据开发文档和其他人的经验一步步总结,最终才能定位问题所在。