NIUCLOUD是一款SaaS管理后台框架多应用插件+云编译。上千名开发者、服务商正在积极拥抱开发者生态。欢迎开发者们免费入驻。一起助力发展! 广告
微信登录有很多第三方可以选择,比如ShareSDK、友盟等,同时能对接完成微博、QQ等登录方式。这篇只说明不使用其他第三方实现微信登录功能。 ## 官方文档 微信登录的官方文档地址为<https://open.weixin.qq.com/cgi-bin/showdocument?action=dir_list&t=resource/res_list&verify=1&id=open1419317851&token=b62b923e6dd0f6b4a00f57e438e764138e36035b&lang=zh_CN> 其中,官方文档上建议将部分操作放到后台接口来实现,原因是 ```bash 1、Appsecret 是应用接口使用密钥,泄漏后将可能导致应用数据泄漏、应用的用户数据泄漏等高风险后果;存储在客户端,极有可能被恶意窃取(如反编译获取Appsecret); 2、access_token 为用户授权第三方应用发起接口调用的凭证(相当于用户登录态),存储在客户端,可能出现恶意获取access_token 后导致的用户数据泄漏、用户微信相关接口功能被恶意发起等行为; 3、refresh_token 为用户授权第三方应用的长效凭证,仅用于刷新access_token,但泄漏后相当于access_token 泄漏,风险同上。 ``` 所以,本篇代码会同时涉及安卓部分和java后台部分。 ## 安卓开发准备 首先需要进入开发者平台注册账号、申请开发者资质等,这部分不是本篇重点内容,略过。访问网址是<https://open.weixin.qq.com/> 进入导航栏中的管理中心,创建移动应用。提交审核的时候说7天内,一般一天就能过。审核通过后可以拿到AppID和AppSecret。以下内容假设这两个数值已经得到。 下载分享需要的jar包并导入项目,下载地址<https://open.weixin.qq.com/cgi-bin/showdocument?action=dir_list&t=resource/res_list&verify=1&id=open1419319167&token=b62b923e6dd0f6b4a00f57e438e764138e36035b&lang=zh_CN> 新建包.wxapi,添加登录页的WXEntryActivity到这个包中。比如包名是com.fymod.easemob,那么WXEntryActivity就位于包com.fymod.easemob.wxapi下。这个是微信登录之后的回调显示页面。 在AndroidManifest.xml中添加的代码一定要加上android:exported="true"和android:launchMode="singleTop"。 ```bash <activity android:name=".wxapi.WXEntryActivity" android:exported="true" android:launchMode="singleTop" > </activity> ``` 另外,添加访问网络权限。要通过后台接口获取数据的。 ```bash <uses-permission android:name="android.permission.INTERNET" /> ``` ## WXEntryActivity代码 WXEntryActivity是一个基本的Activity,不同的是,它需要实现IWXAPIEventHandler接口。 ```bash public class WXEntryActivity extends Activity implements IWXAPIEventHandler { //点击之后跳转到微信页的button private ButtonRectangle btn_login; private IWXAPI api; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_login); //给IWXAPI赋值,第二个参数是AppID api = WXAPIFactory.createWXAPI(this, "wxd167f2424d470758", false); //注册到微信,参数是AppID api.registerApp("wxd167f2424d470758"); //这个必须加上,否则回调显示会出问题 api.handleIntent(getIntent(), this); //这个是登录按钮的点击事件 btn_login = (ButtonRectangle)findViewById(R.id.btn_login); btn_login.setOnClickListener(new View.OnClickListener(){ @Override public void onClick(View v) { //固定方式,写入下面4行代码即可,点击按钮就能跳转到微信了 SendAuth.Req req = new SendAuth.Req(); req.scope = "snsapi_userinfo"; req.state = "STATE"; api.sendReq(req); } }); } //启动微信之前调用这个方法,可以不写代码 @Override public void onReq(BaseReq req) { } //在微信端点击确定登录之后或者取消登录之后,回调方法是onResp @Override public void onResp(BaseResp resp) { switch (resp.errCode) { case BaseResp.ErrCode.ERR_OK: //点击确定登录 //得到code值,需要使用该值和后台交互 String code = JSONObject.parseObject(JSON.toJSONString(resp)).getString("code"); Toast.makeText(WXEntryActivity.this, "登录成功,获取到code:" + code, Toast.LENGTH_LONG).show(); //以下代码需要发送code到后台接口,来获取用户信息,不再赘述 // post to interface, post(code) break; default: //点击的取消,根据自身业务处理 break; } } } ``` ## 获取用户信息 这部分是请求微信端,使用APP获取到的code值来请求详细用户信息。操作分两步: 1 使用code获取到access_token和openid,其中,access_token在下一步请求中作为参数使用,openid是改应用下这个微信号登录的唯一值,用于区分是否是相同用户,也是下一步请求的参数。 2 使用access_token获取详细的用户信息 ```bash public void login(String code, PrintWriter pw) { //自定义的类,就两个参数,int类型处理是否成功,Object类型存放放回的数据 ReturnValue ret = new ReturnValue(); //第一步请求的地址,appid和secret替换成自己申请下来的即可,code就是手机获取到的参数 String url = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=" + "wxd167f2424d470758&secret=a0669d1ae25004db9de44520c519abf5&code=" + code + "&grant_type=authorization_code"; //使用http请求数据,其中,HttpUtil是自己的辅助类,代码会放到后面 String result = HttpUtil.sendGet(url, "utf-8"); System.out.println(result); //我使用的fastjson,可以根据自己使用的jar包调整数据获取格式 JSONObject resultJson = JSONObject.parseObject(result); //处理不正确的code情况,我只判断了400029,如果有完整微信错误字典,可以走框架流程 if(resultJson.containsKey("errcode") && resultJson.getIntValue("errcode") == 40029) { ret.setResult(ReturnValue.FAIL); ret.setData("code不合法"); pw.write(JSON.toJSONString(ret)); return; } if(resultJson.containsKey("access_token")) { //下一步请求需要使用的access_token String access_token = resultJson.getString("access_token"); //该APP下使用微信登录获取到的唯一值 String openid = resultJson.getString("openid"); //使用两个参数请求用户信息 String url2 = "https://api.weixin.qq.com/sns/userinfo?access_token=" + access_token + "&openid=" + openid; //userInfo就是需要的数据,为了方便比对,提供下我获取到的数据 //{"openid":"oHNVDwA8k3H2VQ3mHbXcplVmqm_U","nickname":"昵称改了下","sex":0,"language":"zh_CN","city":"","province":"","country":"CN","headimgurl":"http:\/\/wx.qlogo.cn\/mmopen\/80xzo4yia4MU5IKoUgylvFfRT1QMoVIdlZWs1icEVo3Klkknvy3MOJZuqbetdnqEw1Oibweg7jxBJT3DImlulVf5PYktSUx1QM7\/0","privilege":[],"unionid":"oJT7XwLrV90jbhO9BJSPmMkQ5-8I"} //各个参数的含义可以参见文档。获取到参数之后,根据自己的业务需要选择即可 String userInfo = HttpUtil.sendGet(url2, "utf-8"); ret.setResult(ReturnValue.SUCCESS); ret.setData(userInfo); pw.write(JSON.toJSONString(ret)); } } ``` 处理好用户信息后,APP跳转到主页面即可。 以下是我使用的HttpUtil ```bash import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintWriter; import java.net.URL; import java.net.URLConnection; import java.net.URLEncoder; import java.util.Map; public class HttpUtil { public static String sendGet(String url, String charset) { String result = ""; BufferedReader in = null; try { URL realUrl = new URL(url); URLConnection connection = realUrl.openConnection(); connection.setRequestProperty("accept", "*/*"); connection.setRequestProperty("connection", "Keep-Alive"); connection.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)"); connection.connect(); in = new BufferedReader(new InputStreamReader( connection.getInputStream(), charset)); String line; while ((line = in.readLine()) != null) { result += line; } } catch (Exception e) { System.out.println("发送GET请求出现异常!" + e); e.printStackTrace(); } finally { try { if (in != null) { in.close(); } } catch (Exception e2) { e2.printStackTrace(); } } return result; } public static String sendPostUrl(String url, String param, String charset) { PrintWriter out = null; BufferedReader in = null; String result = ""; try { URL realUrl = new URL(url); URLConnection conn = realUrl.openConnection(); conn.setRequestProperty("accept", "*/*"); conn.setRequestProperty("connection", "Keep-Alive"); conn.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)"); conn.setDoOutput(true); conn.setDoInput(true); out = new PrintWriter(conn.getOutputStream()); out.print(param); out.flush(); in = new BufferedReader(new InputStreamReader( conn.getInputStream(), charset)); String line; while ((line = in.readLine()) != null) { result += line; } } catch (Exception e) { System.out.println("发送 POST 请求出现异常!" + e); e.printStackTrace(); } finally { try { if (out != null) { out.close(); } if (in != null) { in.close(); } } catch (IOException ex) { ex.printStackTrace(); } } return result; } @SuppressWarnings("deprecation") public static String sendPost(String url, Map<String, String> param, String charset) { StringBuffer buffer = new StringBuffer(); if (param != null && !param.isEmpty()) { for (Map.Entry<String, String> entry : param.entrySet()) { buffer.append(entry.getKey()).append("=") .append(URLEncoder.encode(entry.getValue())) .append("&"); } } buffer.deleteCharAt(buffer.length() - 1); PrintWriter out = null; BufferedReader in = null; String result = ""; try { URL realUrl = new URL(url); URLConnection conn = realUrl.openConnection(); conn.setRequestProperty("accept", "*/*"); conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded; charset=utf-8"); conn.setRequestProperty("connection", "Keep-Alive"); conn.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)"); conn.setDoOutput(true); conn.setDoInput(true); out = new PrintWriter(conn.getOutputStream()); out.print(buffer); out.flush(); in = new BufferedReader(new InputStreamReader( conn.getInputStream(), charset)); String line; while ((line = in.readLine()) != null) { result += line; } } catch (Exception e) { System.out.println("发送 POST 请求出现异常!" + e); e.printStackTrace(); } finally { try { if (out != null) { out.close(); } if (in != null) { in.close(); } } catch (IOException ex) { ex.printStackTrace(); } } return result; } } ```