Android集成微信登录的步骤详解
作者:JerryloveEmily 发布时间:2023-01-12 15:12:45
一、首先在Application的onCreate中写:
// GeneralAppliction.java
public static IWXAPI sApi;
@Override
public void onCreate() {
super.onCreate();
sApi = WXEntryActivity.initWeiXin(this, AppConst.WEIXIN_APP_ID);
}
二、在需要登录的地方添加:
// 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里面加入:
<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对象方便操作):
@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中:
// 实现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参数,进一步加强自身授权登录的安全性。
这样客户端使用的地方只要:
WXEntryActivity.loginWeixin(MainActivity.this, GeneralAppliction.sApi);
5. 通过code获取授权口令access_token
我们在onResp的回调方法中获取了code,然后通过code获取授权口令access_token:
/**
* 获取授权口令
*/
private void getAccessToken(String code) {
String url = "https://api.weixin.qq.com/sns/oauth2/access_token?" +
"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是否登录过或者过期的问题:
// 从手机本地获取存储的授权口令信息,判断是否存在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);
}
判断授权口令是否有效:
/**
* 判断accesstoken是过期
* @param accessToken token
* @param openid 授权用户唯一标识
*/
private void isExpireAccessToken(final String accessToken, final String openid) {
String url = "https://api.weixin.qq.com/sns/auth?" +
"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来刷新
/**
* 刷新获取新的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请求地址
String url = "https://api.weixin.qq.com/sns/oauth2/refresh_token?" +
"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获取用户信息
/**
* 获取用户信息
*
/
private void getUserInfo(String access_token, String openid) {
String url = "https://api.weixin.qq.com/sns/userinfo?" +
"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("获取用户信息失败");
}
});
}
通信部分
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() {
// 请求出错,主线程回调
callback.onError(response.code(), response.message());
}
});
} else {
mCallbackHandler.post(new Runnable() {
@Override
public void run() {
try {
// 请求成功,主线程返回请求结果
callback.onSuccess(response.body().string());
} catch (final IOException e) {
// 异常出错,主线程回调
mCallbackHandler.post(new Runnable() {
@Override
public void run() {
callback.onFailure(e);
}
});
}
}
});
}
}
}
});
}
// Api通信回调接口
public interface ApiCallback<T> {
/**
* 请求成功
*
* @param response 返回结果
*/
void onSuccess(T response);
/**
* 请求出错
*
* @param errorCode 错误码
* @param errorMsg 错误信息
*/
void onError(int errorCode, String errorMsg);
/**
* 请求失败
*/
void onFailure(IOException e);
}
总结
集成的详细描述就这样,至于获取的用户信息,小伙伴们应该知道后续自己业务的需求,该怎么处理了。以上就是本文的全部内容了,希望能对大家的学习或者工作带来一定的帮助,如果有疑问大家可以留言交流。


猜你喜欢
- 本文实例为大家分享了C#实现单词本功能的具体代码,供大家参考,具体内容如下看到网上有类似的教程视频实现单词本,于是自己敲了一个实现单词本功能
- 一、前言前面我们讲了Java的入门知识,相信许多小伙伴对Java基础有一个大概的认识了,这也为我们后续的学习打下了基础,所以我们可以继续学习
- 队列和堆栈都是约束版的链表,就像在超市购物,队列是先进先出的数据结构。接着上一篇,派生于链表类List,来模拟一个队列。namespace
- 本文实例为大家分享了UGUI绘制平滑曲线的具体代码,供大家参考,具体内容如下绘制实现自定义的MaskableGraphic挂载在UGUI的U
- 本文实例为大家分享了Android利用Canvas标点画线,并加入位移动画的具体代码,供大家参考,具体内容如下1.背景继上次公司需求实现Ca
- Gson反序列化原理原理简述gson反序列化主要分为两个过程:根据TypeToken创建出对象根据json字符串解析数据,对对象属性赋值对象
- 如何调试Java程序?大家最开始学习Java,都会觉得IDE调试好高端有木有,其实很简单了。下文会尽量简单直观的教会你在Eclipse中调试
- 背景最近好几个项目在运行过程中客户都提出文件上传大小的限制能否设置的大一些,用户经常需要上传好几个G的资料文件,如图纸,视频等,并且需要在上
- 一、首先我们先大致了解一下什么是多线程。(书上的解释)程序是一段静态的代码,它是应用软件的蓝本。进程是程序的一次动态执行过程,对
- C#动态创建lambda表达式近日在使用了一下EF框架,在做多条件where查询的时候不知道怎么做,网上找了找,一开始用context.Da
- Java微信跳一跳操作指南,指哪挑哪。本文的思路是通过adb来控制手机进行操作,通过java写一个jframe覆盖在手机屏幕上,用鼠标获取跳
- 混合开发简介使用Flutter从零开始开发App是一件轻松惬意的事情,但对于一些成熟的产品来说,完全摒弃原有App的历史沉淀,全面转向Flu
- 我们学习网络编程最熟悉的莫过于Http,好,我们就从Http入手,首先我们肯定要了解一下h
- 解决 INSTALL FAILED CONFLICTING PROVIDER的问题方法 在安装Android应用时出现
- @SpringBootApplication的使用1. Spring Boot 的核心1.1. 入口类和 @SpringBootApplic
- 1.memchrmemchr的函数声明:void *memchr(const void *str, int c, size_t n);作用:
- startJVM是加载jvm用的方法。在JPype,apache mod等等很多地方都用到。但凡要用其他语言来加载jvm进程,就要用到这个。
- 在我们的程序当中如果要实现类似《360软件管家》的功能,就要解决两个问题,首先是要判断该程序已有一个实例在运行,其次是要将已运行的应用程序实
- 前言无论是顶部还是底部导航栏,都是大多数APP的标配,网络上的相关实现教程也非常之多。最近回忆起以前写的小项目,发现对这块内容有些遗忘,不妨
- package com.cjonline.foundation.authority.pojo;import java.util