Android集成微信登录的步骤详解
一、首先在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)的下载:可以使用微信分享、登录、收藏、支付等功能需要的库以及文件
下载后把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); }
总结
集成的详细描述就这样,至于获取的用户信息,小伙伴们应该知道后续自己业务的需求,该怎么处理了。以上就是本文的全部内容了,希望能对大家的学习或者工作带来一定的帮助,如果有疑问大家可以留言交流。
上一篇: java自定义实现base64编码转换
下一篇: 详解java中的byte类型