java 实现微信小程序登录态维护的示例代码
这里有新鲜出炉的微信小程序入门, 程序狗速度看过来!
小程序 微信小程序
微信小程序 (weixinxiaochengxu), 简称小程序, 缩写 XCX, 英文名 mini program, 是一种不需要下载安装即可使用的应用, 它实现了应用触手可及的梦想, 用户扫一扫或搜一下即可打开应用
本篇文章主要介绍了 java 实现微信小程序登录态维护的示例代码, 具有一定的参考价值, 有兴趣的可以了解一下
相信不少喜欢开发的朋友都已经知道微信小程序是个什么物种了, 楼主也是从小程序内测期间就开始关注, 并且也写过几个已经上线的微信小程序但是基本上都是写的纯前端, 最近楼主从后端到前端写一个完整的小程序项目, 中间碰到了一些问题, 楼主会找一些个人觉得有学习价值的点不定时的拿出来跟大家分享, 希望对你有一些帮助
本次就从最基本的微信小程序登录态维护开始吧小程序官方 api 文档里面有对登录态的一个完整的解释, 并且有相关的代码想看详情, 可以出门右转: https://mp.weixin.qq.com/debug/wxadoc/dev/api/api-login.html#wxloginobject 我第一次看的时候没怎么看懂, 并且代码没有提供 java 版本的, 这让一个 java 程序员情何以堪, 所以在努力研究了以后决定要做一个 java 版本的简单的 demo 放出来
作为服务端, 如果想获得到使用微信小程序的会员信息, 就需要小程序作为客户端把会员的基本信息传过来类似于手机号, openId 可以作为当前小程序中用户的唯一性标志然而如果把会员的 openId 信息明文直接在服务端与小程序端来回传输的话, 会有安全性的问题万一被别人得到这个 openId, 就相当于得到会员的手机号一样, 就可以做一些其他操作了, 显然是不安全的
为了解决这一问题微信采用了相对安全的方式
- //app.js
- App({
- 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)
- }
- }
- });
- }
- })
微信小程序端会调用 wx.login 的 api, 然后会得到一个 code, 这个 code 对外人来讲是没有任何意义的, 可以放心的传给服务端服务端得到 code 以后, 加上你申请小程序时的 appId, app secret, 去调微信的接口
https://api.weixin.qq.com/sns/jscode2session?appid=APPID&secret=SECRET&js_code=JSCODE&grant_type=authorization_code
就可以得到以下参数:
openid 用户唯一标识
session_key 会话密钥
unionid 本字段在满足一定条件的情况下才返回
其中 openid 就是会员的唯一性标记, 此时服务端可以保存下来
session_key 以后解密 unionId(整个开放平台会员的唯一性标识) 时有用
服务端得到 openid 以后, 为了后边的交互, 要保存下来一般来讲有两种方式: 一种是直接入数据库, 一种是采用效率高一点的缓存楼主采用的是后者, 方式是 redis
按照微信的建议此时需要生成一个不重复值作为 openId 的唯一性标识这里采用的是 java 的 uuid 然后把这个 uuid 值作为 key, 把 openid 以及后面会用到的 session_key 作为 value, 存进 redis 并且把 uuid 值返回给小程序这样小程序就可以直接拿 uuid 值跟服务端交互
也许会有人问, 如果有人得到 uuid 值其实跟得到 openid 没什么区别啊, 都相当于是会员的唯一性标志
所以这里要对这个 uuid 值进行一个处理首先存入 redis 时要有时效性 session_key 在微信服务器有效期是 30 天, 建议服务端缓存 session_key 不超过 30 天当小程序传过来的 uuid 值过期时, 认为这是过期的 uuid, 则重新走 wx.login 步骤
为了方便 redis 中不仅会寸 uuid 与 openid 的对应关系还会再存一条 openid 对应 uuid 的记录, 目的是为了下一次重新 wx.login 步骤时根据 openid 找到之前老的 uuid, 如果存在的话就删掉, 然后查询一条新的 uuid 值, 并且把 openid 对应的这条记录也更新掉这样 redis 服务器中就不会有多余的脏数据, 减轻服务器的负担
以上就是我理解的整个登录态的过程, 当然还有 wx.checkSession 这些没有讲到, 其实就是发现 session_key 失效是再重新走一遍上述的流程就可以了所以没有仔细说不知道我有没有讲清楚我会把整个流程的关键代码贴出来, 供大家参考
- @ActionKey("/loginByWeixin") public void loginByWeixin() throws Exception {
- logger.info("Start getSessionKey");
- String json = HttpKit.readData(getRequest());
- JSONObject reqJson = JSON.parseObject(json);
- String jsCode = reqJson.getString("code");
- if (jsCode == null || "".equals(jsCode)) {
- logger.info("缺少必要参数");
- renderJson(new OutRoot().setCode("100").setMsg(SYS.PARAMETER_FAIL));
- } else {
- List < Record > record = appInfoService.selectAppInfo();
- String appId = record.get(0).get("app_id");
- String appSecret = record.get(0).getStr("app_secret");
- if (appId == null || "".equals(appId) || appSecret == null || "".equals(appSecret)) {
- logger.info("缺少必要参数");
- renderJson(new OutRoot().setCode("100").setMsg(SYS.PARAMETER_FAIL));
- } else {
- String url = "https://api.weixin.qq.com/sns/jscode2session";
- String httpUrl = url + "?appid=" + appId + "&secret=" + appSecret + "&js_code=" + jsCode + "&grant_type=authorization_code";
- String ret = HttpRequest.sendGetRequest(httpUrl);
- logger.info("微信返回的结果 {}", ret);
- if (ret == null || "".equals(ret)) {
- logger.info("网络超时");
- renderJson(new OutRoot().setCode("101").setMsg(SYS.CONTACT_FAIL));
- } else {
- JSONObject obj = JSONObject.parseObject(ret);
- if (obj.containsKey("errcode")) {
- String errcode = obj.get("errcode").toString();
- logger.info("微信返回的错误码 {}", errcode);
- renderJson(new OutRoot().setCode("101").setMsg(SYS.CONTACT_FAIL));
- } else if (obj.containsKey("session_key")) {
- logger.info("调微信成功");
- // 开始处理 userInfo
- String openId = obj.get("openid").toString();
- Record tbMember = new Record();
- tbMember.set("weixin_openid", openId);
- System.out.println("openId==" + openId);
- // 先查询 openId 存在不存在, 存在不入库, 不存在就入库
- List < Record > memberList = tbMemberService.selectMember(tbMember);
- if (memberList != null && memberList.size() > 0) {
- logger.info("openId 已经存在, 不需要插入");
- } else {
- JSONObject rawDataJson = reqJson.getJSONObject("userInfo");
- String nickName = rawDataJson.getString("nickName");
- String avatarUrl = rawDataJson.getString("avatarUrl");
- String gender = rawDataJson.getString("gender");
- String province = rawDataJson.getString("province");
- String city = rawDataJson.getString("city");
- String country = rawDataJson.getString("country");
- tbMember.set("gender", gender);
- tbMember.set("nick_name", nickName);
- tbMember.set("avatar_url", avatarUrl);
- Long openId2 = tbMemberService.addMember(tbMember);
- logger.info("openId 不存在, 插入数据库");
- }
- // (1) 获得 sessionkey
- String sessionKey = obj.get("session_key").toString();
- logger.info("sessionKey==" + sessionKey);
- logger.info("openId==" + openId);
- // (2) 得到 sessionkey 以后存到缓存, key 值采用不会重复的 uuid
- String rsession = UUID.randomUUID().toString();
- Cache tokenCache = Redis.use("redis_00");
- // (3) 首先根据 openId, 取出来之前存的 openId 对应的 sessionKey 的值
- String oldSeesionKey = tokenCache.getJedis().get(openId);
- if (oldSeesionKey != null && !"".equals(oldSeesionKey)) {
- logger.info("oldSeesionKey==" + oldSeesionKey);
- // (4) 删除之前 openId 对应的缓存
- tokenCache.getJedis().del(oldSeesionKey);
- logger.info("老的 openId 删除以后 ==" + tokenCache.getJedis().get(oldSeesionKey));
- }
- // (5) 开始缓存新的 sessionKey: key --> uuid, value --> sessionObj
- JSONObject sessionObj = new JSONObject();
- sessionObj.put("openId", openId);
- sessionObj.put("sessionKey", sessionKey);
- tokenCache.getJedis().set(rsession, sessionObj.toJSONString());
- // (6) 开始缓存新的 openId 与 session 对应关系 : key --> openId , value --> rsession
- tokenCache.getJedis().set(openId, rsession);
- String newOpenId = tokenCache.getJedis().get(openId);
- String newrSession = tokenCache.getJedis().get(rsession);
- logger.info("新的 openId==" + newOpenId);
- logger.info("新的 newrSession==" + newrSession);
- // (7) 把新的 sessionKey 返回给小程序
- JSONObject objret = new JSONObject();
- objret.put("rdSessionKey", rsession);
- objret.put("errno", 0);
- renderJson(objret);
- }
- }
- }
- }
- }
项目框架是我比较喜欢 Jfinal,java 轻量级急速开发框架, 非常高效, 也推荐给大家可能有哪些遗漏的地方欢迎大家积极提出意见和批评
来源: http://www.phperz.com/article/18/0211/353169.html