前言:最近自己一直再弄微信小程序,磕磕绊绊中也算把小程序的java接口开发走了一遍,这里总结一下自己学习过的知识,多积累才不容易忘记。这是第一篇的微信小程序java接口开发博客,会按照一个小程序从登录到请求接口返回数据,到最后的微信小程序支付的流程完成总结。
微信官方文档:小程序-开放接口-登录
一、小程序wx.login(OBJECT)
注:这里很不要脸的直接抄袭微信小程序API接口文档
1 wx.login(OBJECT)
调用接口获取登录凭证(code)进而换取用户登录态信息,包括用户的唯一标识(openid) 及本次登录的 会话密钥(session_key)。用户数据的加解密通讯需要依赖会话密钥完成。
(1)OBJECT参数说明:
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| success | Function | 否 | 接口调用成功的回调函数 |
| fail | Function | 否 | 接口调用失败的回调函数 |
| complete | Function | 否 | 接口调用结束的回调函数(调用成功、失败都会执行) |
(2)success返回参数说明:
| 参数名 | 类型 | 说明 |
|---|---|---|
| errMsg | String | 调用结果 |
| code | String | 用户允许登录后,回调内容会带上 code(有效期五分钟),开发者需要将 code 发送到开发者服务器后台,使用code 换取 session_key api,将 code 换成 openid 和 session_key |
(3)示例代码:
//app.jsApp({ onLaunch: function() { wx.login({ success: function(res) { if (res.code) { //发起网络请求 wx.request({ url: 'https://test.com/onLogin', data: { code: res.code } }) } else { console.log('获取用户登录态失败!' + res.errMsg) } } }); }})二、java接口
1 code 换取 openid和session_key
这是一个 HTTPS 接口,开发者服务器使用登录凭证 code 获取 session_key 和 openid。其中 session_key 是对用户数据进行加密签名的密钥。
(1)接口地址:
https://api.weixin.qq.com/sns/jscode2session?appid=APPID&secret=SECRET&js_code=JSCODE&grant_type=authorization_code(2)请求参数:
| 参数 | 必填 | 说明 |
|---|---|---|
| appid | 是 | 小程序唯一标识 |
| secret | 是 | 小程序的 app secret |
| js_code | 是 | 登录时获取的 code |
| grant_type | 是 | 填写为 authorization_code |
(3)返回参数:
| 参数 | 说明 |
|---|---|
| openid | 用户唯一标识 |
| session_key | 会话密钥 |
(4)返回说明:
//正常返回的JSON数据包{ "openid": "OPENID", "session_key": "SESSIONKEY"}//错误时返回JSON数据包(示例为Code无效){ "errcode": 40029, "errmsg": "invalid code"}2 java接口
(1)Controller接口类
/** * 获取openId * 开发人员:wangql * 开发时间:2017-6-14 * @param params * @param request * @param response */ @ResponseBody @RequestMapping(value="/getOpenId",method = RequestMethod.POST) public MapString, Object getOpenId(HttpServletRequest request,HttpServletResponse response){ MapString, Object map = new HashMapString, Object(); String status = "1"; String msg = "ok"; String WX_URL = "https://api.weixin.qq.com/sns/jscode2session?appid=APPID&secret=SECRET&js_code=JSCODE&grant_type=authorization_code"; try { String code = request.getParameter("code"); if(StringUtils.isBlank(code)){ status = "0";//失败状态 msg = "code为空"; }else { String requestUrl = WX_URL.replace("APPID", WeixinConstants.APPID). replace("SECRET", WeixinConstants.APP_SECRECT).replace("JSCODE", code). replace("authorization_code", WeixinConstants.AUTHORIZATION_CODE); logger.info(requestUrl); // 发起GET请求获取凭证 JSONObject jsonObject = CommonUtil.httpsRequest(requestUrl, "GET", null); if (jsonObject != null) { try { map.put("openid", jsonObject.getString("openid")); map.put("session_key", jsonObject.getString("session_key")); } catch (JSONException e) { // 获取token失败 status = "0"; msg = "code无效"; } }else { status = "0"; msg = "code无效"; } } map.put("status", status); map.put("msg", msg); } catch (Exception e) { logger.error(e.getMessage(),e); return AnalyzeMoblieData.errorResponse(); } return map; }(2)http请求工具类
import java.io.BufferedReader;import java.io.IOException;import java.io.InputStream;import java.io.InputStreamReader;import java.io.OutputStream;import java.io.UnsupportedEncodingException;import java.net.ConnectException;import java.net.URL;import java.util.Iterator;import java.util.Map;import javax.net.ssl.HttpsURLConnection;import javax.net.ssl.SSLContext;import javax.net.ssl.SSLSocketFactory;import javax.net.ssl.TrustManager;import org.apache.commons.httpclient.HttpClient;import org.apache.commons.httpclient.HttpMethod;import org.apache.commons.httpclient.NameValuePair;import org.apache.commons.httpclient.methods.PostMethod;import org.apache.commons.httpclient.params.HttpMethodParams;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import net.sf.json.JSONException;import net.sf.json.JSONObject;/** * 类名: CommonUtil.java/br * 描述: http请求工具类/br * 开发人员:wangql/br * 创建时间: 2017-6-12/br */public class CommonUtil { private static Logger log = LoggerFactory.getLogger(CommonUtil.class); /** * 发送https请求 * @param requestUrl 请求地址 * @param requestMethod 请求方式(GET、POST) * @param outputStr 提交的数据 * @return JSONObject(通过JSONObject.get(key)的方式获取json对象的属性值) */ public static JSONObject httpsRequest(String requestUrl, String requestMethod, String outputStr) { JSONObject jsonObject = null; try { // 创建SSLContext对象,并使用我们指定的信任管理器初始化 TrustManager[] tm = { new MyX509TrustManager() }; SSLContext sslContext = SSLContext.getInstance("SSL", "SunJSSE"); sslContext.init(null, tm, new java.security.SecureRandom()); // 从上述SSLContext对象中得到SSLSocketFactory对象 SSLSocketFactory ssf = sslContext.getSocketFactory(); URL url = new URL(requestUrl); HttpsURLConnection conn = (HttpsURLConnection) url.openConnection(); conn.setSSLSocketFactory(ssf); conn.setDoOutput(true); conn.setDoInput(true); conn.setUseCaches(false); // 设置请求方式(GET/POST) conn.setRequestMethod(requestMethod); // 当outputStr不为null时向输出流写数据 if (null != outputStr) { OutputStream outputStream = conn.getOutputStream(); // 注意编码格式 outputStream.write(outputStr.getBytes("UTF-8")); outputStream.close(); } // 从输入流读取返回内容 InputStream inputStream = conn.getInputStream(); InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "utf-8"); BufferedReader bufferedReader = new BufferedReader(inputStreamReader); String str = null; StringBuffer buffer = new StringBuffer(); while ((str = bufferedReader.readLine()) != null) { buffer.append(str); } // 释放资源 bufferedReader.close(); inputStreamReader.close(); inputStream.close(); inputStream = null; conn.disconnect(); jsonObject = JSONObject.fromObject(buffer.toString()); } catch (ConnectException ce) { log.error("连接超时:{}", ce); } catch (Exception e) { log.error("https请求异常:{}", e); } return jsonObject; }}(3)信任管理器类
import java.security.cert.CertificateException;import java.security.cert.X509Certificate;import javax.net.ssl.X509TrustManager;/** * 类名: MyX509TrustManager.java/br * 描述: 信任管理器/br * 开发人员:wangql/br * 创建时间: 2017-6-12/br */public class MyX509TrustManager implements X509TrustManager { // 检查客户端证书 public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { } // 检查服务器端证书 public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { } // 返回受信任的X509证书数组 public X509Certificate[] getAcceptedIssuers() { return null; }}3 登录态维护
通过 wx.login() 获取到用户登录态之后,需要维护登录态。开发者要注意不应该直接把 session_key、openid 等字段作为用户的标识或者 session 的标识,而应该自己派发一个 session 登录态(请参考登录时序图)。对于开发者自己生成的 session,应该保证其安全性且不应该设置较长的过期时间。session 派发到小程序客户端之后,可将其存储在 storage ,用于后续通信使用。
总结:其实小程序获取openid和session_key原理非常简单,就是按照指定地址指定参数发送http的get请求。我们只要按照接口文档先使用wx.login获取js_code,然后再向https://api.weixin.qq.com/sns/jscode2session地址发送带appid、secret、js_code和grant_type的参数即可。关键是java如何发送安全的http请求,这里是关键的地方。
参考文章
微信小程序API 登录













