微信小程序现在被普遍使用,微信小程序按照微信官网的定义来讲就是:前端
微信小程序是一种全新的链接用户与服务的方式,它能够在微信内被便捷地获取和传播,同时具备出色的使用体验。java
这就是微信小程序的魅力所在,有的时候咱们不须要去下载过多的app,只要打开微信,就能够应用每个服务,甚至连注册都变得简单起来,具备特别贴近人心的用户体验。redis
最近在作一个微信小程序的服务端,主要就是实现登陆、业务帐号和微信号绑定的基本功能,接下来总结一下如何实现微信小程序的服务端代码。算法
要去实现服务端的代码,就要先去了解客户端的请求。数据库
微信小程序开发平台开发了很全面、功能很强大的开发API供咱们使用:json
https://developers.weixin.qq.com/miniprogram/dev/api/小程序
咱们来看下api中的登陆功能:微信小程序
这个是微信小程序登陆的时序图,很是明显地告知了咱们微信小程序登陆的请求服务调用的过程api
1.添加微信小程序固定信息跨域
在注册的时候,这个小程序的appId和appSecret已经知道了,还有固定的url,而后咱们能够将其作为配置文件放入后台的代码中去:
2.根据小程序信息和code获取openId和session_key
在前端代码中,每当咱们刚进入小程序的时候,都会去调用wx.login()方法,会获得一个code,而后发送给咱们,而后,咱们经过code和这些已知的参数,去调用微信的接口,去获取openId和session_key,做为登陆态的标识
/** * 获取微信小程序的session_key和openid * * @author hengyang4 * @param code 微信前端login()方法返回的code * @return jsonObject * * */ public JSONObject getSessionKeyAndOpenId(String code)throws Exception{ //微信登陆的code值 String wxCode = code; //读取属性文件 ResourceBundle resourceBundle = ResourceBundle.getBundle("weixin"); //服务器端调用接口的url String requestUrl = resourceBundle.getString("url"); //封装须要的参数信息 Map<String,String> requestUrlParam = new HashMap<String,String>(); //开发者设置中的appId requestUrlParam.put("appid",resourceBundle.getString("appId")); //开发者设置中的appSecret requestUrlParam.put("secret",resourceBundle.getString("appSecret")); //小程序调用wx.login返回的code requestUrlParam.put("js_code", wxCode); //默认参数 requestUrlParam.put("grant_type", "authorization_code"); JSONObject jsonObject = JSON.parseObject(sendPost(requestUrl,requestUrlParam)); return jsonObject; } /** * 向指定 URL 发送POST方法的请求 * * @param url 发送请求的 URL * @return 所表明远程资源的响应结果 */ public String sendPost(String url, Map<String, ?> paramMap) { PrintWriter out = null; BufferedReader in = null; String result = ""; String param = ""; Iterator<String> it = paramMap.keySet().iterator(); while(it.hasNext()) { String key = it.next(); param += key + "=" + paramMap.get(key) + "&"; } try { URL realUrl = new URL(url); // 打开和URL之间的链接 URLConnection conn = realUrl.openConnection(); // 设置通用的请求属性 conn.setRequestProperty("accept", "*/*"); conn.setRequestProperty("connection", "Keep-Alive"); conn.setRequestProperty("Accept-Charset", "utf-8"); conn.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)"); // 发送POST请求必须设置以下两行 conn.setDoOutput(true); conn.setDoInput(true); // 获取URLConnection对象对应的输出流 out = new PrintWriter(conn.getOutputStream()); // 发送请求参数 out.print(param); // flush输出流的缓冲 out.flush(); // 定义BufferedReader输入流来读取URL的响应 in = new BufferedReader(new InputStreamReader(conn.getInputStream(), "UTF-8")); String line; while ((line = in.readLine()) != null) { result += line; } } catch (Exception e) { log.error(e.getMessage(),e); } //使用finally块来关闭输出流、输入流 finally{ try{ if(out!=null){ out.close(); } if(in!=null){ in.close(); } } catch(IOException ex){ ex.printStackTrace(); } } return result; }
3. 将openId和session_key生成sessionId返回客户端
第三方服务器端拿到请求回来的session_key和openid,先留着,不能给客户端;而后用操做系统提供的真正随机数算法生成一个新的session,叫session_id
· 因此,每次前端wx.login()后调用的服务端的controller咱们就应该这样写:
@RequestMapping(value = "/loginByWeixin", produces = "application/json;charset=UTF-8") public String loginByWeixin(@RequestParam("code") String code) throws Exception{ //获得用户的openId + sessionKey JSONObject jsonObject = getSessionKeyAndOpenId(code); log.info(jsonObject.toString()); System.out.println(jsonObject); String openId = jsonObject.getString("openid"); String sessionKey = jsonObject.getString("session_key"); //组装结果 Map<String,Object> resMap = new HashMap<String,Object>(); //判断openId是否存在用户表中 UserInfo userInfo = userInfoMapper.selectByUserName(openId); if(ValidateUtil.isEmpty(userInfo)){ //不存在该用户关联关系 log.info("验证是否绑定微信","未登陆"); resMap.put("code",ErrorCode.ERR_WEIXIN_USER_EMPTY.getErrorCode()); resMap.put("desc",ErrorCode.ERR_WEIXIN_USER_EMPTY.getErrorMessage()); }else { //组装结果 log.info("验证是否绑定微信", "用户查询成功"); //0:操做成功 resMap.put("code", ErrorCode.ERR_SUCCEED.getErrorCode()); resMap.put("desc", ErrorCode.ERR_SUCCEED.getErrorMessage()); resMap.put("userInfo", userInfo); } //将经过md5生成sessionId(通常是用个操做系统提供的真正随机数算法生成新的session) String sessionId = MD5.EncodeByMd5(openId+sessionKey+ DateUtil.getNowTime()); jedisCache.hashSet(sessionId,"ses",openId); resMap.put("sessionId",sessionId); return JSONConvertor.toJSON(resMap); }
4. 使用过滤器过滤请求头中含有session_id的请求
而后咱们将session_id为key,微信服务端返回的openId为值,保存起来,这里咱们用了redis去缓存这个session信息,openId为咱们系统用户与微信绑定的标识
接下来,咱们每次请求的时候,都会将session_id放入请求头中去,而后判断在redis中是否有key为该值得键值对,从而判断用户session是否失效
咱们这里使用了过滤器来拦截用户请求
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)throws IOException,ServletException{ //设置跨域请求 HttpServletResponse resp = (HttpServletResponse)response; resp.setHeader("Access-Control-Allow-Origin", "*"); HttpServletRequest req = (HttpServletRequest)request; //获取请求时的sessionId String sessionId = req.getHeader("sessionId"); if(StrUtil.IsNullOrEmpty(sessionId)){ //该请求不须要验证session,直接经过 log.info("sessionId过滤","该请求不须要过滤,经过"); chain.doFilter(request,response); return; }else { //只有在缓存中存在该sessionId才能进行请求 if (!jedisCache.existKey(sessionId)) { // 登陆信息已过时,请从新登陆 log.info("sessionId过滤", "登陆信息失效,请从新登陆"); response.getWriter().write("登陆信息失效,请从新登陆"); return; } log.info("sessionId过滤", "session验证成功"); chain.doFilter(request, response); } }
5. 在数据库中创建用户与微信用户惟一标识的关联关系
每当用户注册或者登录时,请求中都会含有session_id,而后咱们将session_id做为key去redis中查找,获得value值,也就是咱们以前存的openId,而后咱们将openId和用户信息进行数据库表中的关联,以后,咱们调用登录方法的时候,若是该openId有关联的用户信息,则不须要去登陆,直接给前端返回用户信息便可,就是咱们以前的那段代码:
在这里,咱们的微信小程序服务端代码就基本实现了,能够用于微信小程序与业务系统的登陆,微信小程序的功能还有不少,以后咱们有机会多去试一下其余功能,感觉小程序的强大和快捷。