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,appsecret,去调微信的接口
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") publicvoidloginByWeixin()throwsException{ logger.info("StartgetSessionKey"); Stringjson=HttpKit.readData(getRequest()); JSONObjectreqJson=JSON.parseObject(json); StringjsCode=reqJson.getString("code"); if(jsCode==null||"".equals(jsCode)){ logger.info("缺少必要参数"); renderJson(newOutRoot().setCode("100").setMsg(SYS.PARAMETER_FAIL)); }else{ Listrecord=appInfoService.selectAppInfo(); StringappId=record.get(0).get("app_id"); StringappSecret=record.get(0).getStr("app_secret"); if(appId==null||"".equals(appId)||appSecret==null||"".equals(appSecret)){ logger.info("缺少必要参数"); renderJson(newOutRoot().setCode("100").setMsg(SYS.PARAMETER_FAIL)); }else{ Stringurl="https://api.weixin.qq.com/sns/jscode2session"; StringhttpUrl=url+"?appid="+appId+"&secret="+appSecret+"&js_code="+jsCode +"&grant_type=authorization_code"; Stringret=HttpRequest.sendGetRequest(httpUrl); logger.info("微信返回的结果{}",ret); if(ret==null||"".equals(ret)){ logger.info("网络超时"); renderJson(newOutRoot().setCode("101").setMsg(SYS.CONTACT_FAIL)); }else{ JSONObjectobj=JSONObject.parseObject(ret); if(obj.containsKey("errcode")){ Stringerrcode=obj.get("errcode").toString(); logger.info("微信返回的错误码{}",errcode); renderJson(newOutRoot().setCode("101").setMsg(SYS.CONTACT_FAIL)); }elseif(obj.containsKey("session_key")){ logger.info("调微信成功"); //开始处理userInfo StringopenId=obj.get("openid").toString(); RecordtbMember=newRecord(); tbMember.set("weixin_openid",openId); System.out.println("openId=="+openId); //先查询openId存在不存在,存在不入库,不存在就入库 List memberList=tbMemberService.selectMember(tbMember); if(memberList!=null&&memberList.size()>0){ logger.info("openId已经存在,不需要插入"); }else{ JSONObjectrawDataJson=reqJson.getJSONObject("userInfo"); StringnickName=rawDataJson.getString("nickName"); StringavatarUrl=rawDataJson.getString("avatarUrl"); Stringgender=rawDataJson.getString("gender"); Stringprovince=rawDataJson.getString("province"); Stringcity=rawDataJson.getString("city"); Stringcountry=rawDataJson.getString("country"); tbMember.set("gender",gender); tbMember.set("nick_name",nickName); tbMember.set("avatar_url",avatarUrl); LongopenId2=tbMemberService.addMember(tbMember); logger.info("openId不存在,插入数据库"); } //(1)获得sessionkey StringsessionKey=obj.get("session_key").toString(); logger.info("sessionKey=="+sessionKey); logger.info("openId=="+openId); //(2)得到sessionkey以后存到缓存,key值采用不会重复的uuid Stringrsession=UUID.randomUUID().toString(); CachetokenCache=Redis.use("redis_00"); //(3)首先根据openId,取出来之前存的openId对应的sessionKey的值。 StringoldSeesionKey=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 JSONObjectsessionObj=newJSONObject(); 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); StringnewOpenId=tokenCache.getJedis().get(openId); StringnewrSession=tokenCache.getJedis().get(rsession); logger.info("新的openId=="+newOpenId); logger.info("新的newrSession=="+newrSession); //(7)把新的sessionKey返回给小程序 JSONObjectobjret=newJSONObject(); objret.put("rdSessionKey",rsession); objret.put("errno",0); renderJson(objret); } } } } }
项目框架是我比较喜欢Jfinal,java轻量级急速开发框架,非常高效,也推荐给大家。可能有哪些遗漏的地方欢迎大家积极提出意见和批评。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。