最近团队在开发一款小程序,都是新手,一边看文档,一边开发。在开发中会遇到各类问题,今天把小程序登陆这块的流程整理下,作个记录。html
小程序的登陆跟平时本身APP这种登陆验证还不太同样,多了一个角色,那就是微信服务器。redis
根据微信官方提供的登陆流程时序图能够清楚的了解小程序登陆须要多少个步骤,下面咱们来总结下:json
首先code是微信给的,若是你随意生成code去验证确定是无效的,只有微信给的code才有效。code传到开发者本身的服务后,再去问微信:小程序
Hi 哥们,我这个code是有效的仍是无效的啊?后端
微信会告诉你是有效仍是无效,有效的状况下还会给你一个用户的标识,也就是openid,同时还会有一个session_key,也就是会话的key。session_key的有效期默认是2小时,当用户一直在使用小程序的话会自动刷新,这个是由微信这边来维护的。api
注意:安全
session_key
是对用户数据进行 加密签名 的密钥。为了应用自身的数据安全,开发者服务器不该该把会话密钥下发到小程序,也不该该对外提供这个密钥。因此咱们要为session_key建立别名,这个别名关联的哪一个用户只有咱们本身知道,惟一须要作的工做就在这块。bash
我推荐2种方式来作关联:服务器
第一种: 随机生成key, 关联openid,存入redis中,当请求带入key,直接从redis中获取openid获得当前用户信息,这个其实也就是咱们本身去维护了会话信息微信
第二种: 采用JWT生成token,将openid绑定到token中,将token返回给小程序,请求的时候带上token,经过解析token获得用户信息。
下面咱们以第二种方式来进行讲解,会贴上部分代码:
小程序中在app.js中的onLaunch方法中增长获取code方法,而且调用后端的登陆接口获取token:
wx.login({ success: function (res) { var code = res.code; if (code) { console.log('app启动获取用户登陆凭证:' + code); let params = { "code": code }; let result = config.requestHttp(config.url.userLogin, 'POST', params) result.then(res => { let data = res.data if (data.code == 200) { wx.setStorageSync("login_token", data.data.token); } }).catch(err => { console.log(err) }); } else { console.log('获取用户登陆态失败:' + res.errMsg); } } }) 复制代码
userLogin接口则根据小程序的code去调用微信接口验证:
// 小程序获取SessionKey接口地址 String loginUrl = "https://api.weixin.qq.com/sns/jscode2session"; String url = loginUrl + "?appid=%s&secret=%s&grant_type=%s&js_code=%s"; url = String.format(url, appid, appSecret, grantType, param.getCode()); String result = restTemplate.getForObject(url, String.class); Map<String, Object> map = JsonUtils.toBean(Map.class, result); // 请求成功 if (map.containsKey("session_key")) { String openid = map.get("openid").toString(); // 第一次保存到用户表,生成JWT TOKEN返回 } 复制代码
小程序端须要将 wx.request()封装成一个通用的方法,全部跟后台交互都用这个方法来调用接口,咱们能够在这个方法中设置登陆以后获取的Token。这样每次请求都会将Token塞到请求头中,咱们在网关中就能够获取这个Token进行解析验证。
//请求封装 function requestHttp(url, method, data) { //请求头设置 var header = { Authorization: wx.getStorageSync("login_token") } return new Promise((resolve, reject) => { wx.request({ url: config.home_config + url, data: data, header: header, method: method, success: (res => { if (res.data.code === 200) { resolve(res) } else { reject(res) } }), fail: (res => { reject(res) }) }) }) } 复制代码
Zuul中进行验证:
RequestContext ctx = RequestContext.getCurrentContext(); HttpServletRequest request = ctx.getRequest(); String token = request.getHeader("Authorization"); if (StringUtils.isBlank(token)) { ctx.setSendZuulResponse(false); ctx.set("isSuccess", false); ctx.setResponseBody(JsonUtils.toJson(Response.fail("非法请求【缺乏Authorization】", ResponseCode.NO_AUTH_CODE))); ctx.getResponse().setContentType("application/json; charset=utf-8"); return null; } // 验证Token是否有效 JWTResult jwResult = JWTUtils.checkToken(token); if (!jwResult.isStatus()) { ctx.setSendZuulResponse(false); ctx.set("isSuccess", false); ctx.setResponseBody(JsonUtils.toJson(Response.fail(jwResult.getMsg(), jwResult.getCode()))); ctx.getResponse().setContentType("application/json; charset=utf-8"); return null; } ctx.addZuulRequestHeader("loginUserId", jwResult.getUid()); return null; 复制代码
验证成功后将用户ID设置到请求头中,传递给后端服务使用。
使用JWT必然有一个问题是Token的失效问题,我这边失效时间设置的为2个小时,正常的话用户打开小程序,使用不可能连续超过2个小时,登陆的逻辑是在app.js中作的,只要下次进去token就会从新申请。不过这个也能够调整,好比稍微长一点。
核心就是用户的认证交给了微信,只要微信告诉咱们认证成功了,咱们就能够本身接管会话信息了。