摘要

关于微信开发的话题,例子确实已经有不少,但大部分都是人云亦云,很多小细节或者需要注意的地方却大多没有讲清楚,这令很多刚开始开发的人感觉大很迷茫。而我今天要说的话题,主要着眼于两个方面。

一:如何存储获取用户信息及调用第三方接口所需要的token.

二 : 第三方页面授权,如何减少从微信服务器获取用户openid的次数以及减少获取用户信息的次数,加速第三方页面的加载速速。

(注:演示所使用的是java语言,其他语言可与此类似)


下面我将开始讲述第一个问题。

如何存储获取用户信息及调用第三方接口所需要的token?

从微信的官方文档上,我们知道,获取token的次数为1天2000次,每两小时token失效一次,对于那种一天没有几个人访问的微信公众号而言,他们可能只是简单的每次调用高级接口都会去获取一遍token.众所周知,这种做法极其的耗费时间。从网上其他网友给出的存储方案大概有如下几种:

  1. 数据库:通过微信接口获取到 Token 之后,将 Token存储到数据库,每次需要时从数据库取出。采用定时任务的方法每隔一个固定的时间去获取一次token.
  2. NoSQl:这里以 Redis 为例子。通过微信接口获取到 Token 之后,存入 Redis,可以通过设置redis的过期时间,每次需要token时从redis中取出来,若没有,则证明Token 已过期可重新获取(当然也可采用上面的定时任务的方式定期获取)。
  3. 文件存储:这个比较适合单一公众号的情况。通过微信接口获取到 Token 之后,存入文件,采用定时任务的方法每隔一个固定的时间去获取一次token.
(固定的时间:一般设为1小时为宜,如1小时后因网络原因,请求获取token失败,则原有的token还可以在使用1小时,这种方式将错误降低了一半)
大致有这三种方案。当然对于那些将token存储在session或者cookie里面的,这里我只能呵呵一笑了。
基于以上三种方式,我个人比较推崇的一个顺序是NoSQl>数据库>文件存储。
下面我分别来说说他们的优缺点:
采用数据库和文件存储,对于单节点并且用户请求数不是很多的web项目而言,是可以的正常运行的,但是对于分布式多节点的项目,采用这两种方式是行不通的。而我今天要说的第三种通过Redis方法存储,一方面它可以提升获取token的速度,另一方面当分布式项目的多个节点要公用同一个token的时候,我们可以方便的取到。

第三方页面授权,如何减少从微信服务器获取用户openid的次数以及减少获取用户信息的次数,加速第三方页面的加载速速?

从诸多的博客中,我们了解到,第三方页面授权获取用户信息,我们要调用两次微信接口。

  • 第一次:构造应用授权的url,通过返回的code,换取用户的openid.
  • 第二次:通过用户的openid与token获取用户信息。

对于页面比较多的应用,每个页面请求时都需要调用两个方法,于用户而言这是极其耗费时间的。

然而诸多的博客只是告诉我们改如何处理用户信息,诸如将用户信息先拉取下来存储到自己的数据库,然后每次需要时从自己的数据库中通过openid来获取。殊不知这种博客只说了一点,他们没有考虑到的问题是:

  1. 用户若修改了自己的信息,该如何同步到自己的表里面.
  2. 用户的信息是获取到了,但是每次用户访问网页,标识用户身份的openid依旧每次都要去调用接口获取。(获取用户信息的次数微信API规定为500000次)

那么接下来我要说的这个就是如何解决上面两个问题,处理过程大致如下:

a.添加拦截器,拦截需要授权页面的controller

拦截器:

package com.fdc.home.dec.wx.filter;

import com.alibaba.dubbo.common.utils.StringUtils;
import com.fdc.platform.common.yfutil.PropertyReader;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter; import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Arrays; /**
* Created by pl on 2017/1/13.
*/
public class OAuth2Interceptor extends HandlerInterceptorAdapter { public static String indexUrl = PropertyReader.getValue("indexUrl");//从配置文件中读取域名
// private static String[] arrQueController = {"newquestion", "mycenter","testCookie"};
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
Cookie[] cookies = request.getCookies();
String openId=null;
//判断cookie中是否存在openid 若存在则直接跳过,不存在则获取一次
if(cookies!=null){
for(Cookie cookie : cookies){
if(cookie.getName().equals("openId")){
openId = cookie.getValue();
}
}
}
if (StringUtils.isEmpty(openId)) {
if (handler instanceof HandlerMethod) {
HandlerMethod handlerMethod = (HandlerMethod) handler;
String methodName = handlerMethod.getMethod().getName();
String uri = request.getRequestURI();
// if (checkList(arrQueController, methodName)) {
// System.out.println("执行了");
response.sendRedirect(indexUrl + "/oauth2Api?resultUrl=" + indexUrl + uri);
return false;
// }
}
return true;
} else {
return true;
}
} public void postHandle(
HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)
throws Exception {
} public void afterCompletion(
HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
throws Exception {
} public boolean checkList(String[] arr, String targetValue) {
return Arrays.asList(arr).contains(targetValue);
} }

spring-servlet.xml (拦截两个指定的controller:testCookie,testCookie1)

<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/testCookie"/>
<mvc:mapping path="/testCookie1"/>
<bean class="com.fdc.home.dec.wx.filter.OAuth2Interceptor"/>
</mvc:interceptor>
</mvc:interceptors>

b.用户首次访问,调用微信接口获取openid和用户信息。将openid写入服务器端的cookie,将用户的信息写入redis缓存中,以openid作为redis的key。

package com.fdc.home.dec.wx.controller;

import com.fdc.home.dec.service.inter.service.DecWxService;
import com.fdc.home.dec.wx.service.CheckUserInfo;
import com.fdc.home.dec.wx.utils.JSONHelper;
import com.fdc.home.dec.wx.utils.WxUtils;
import com.fdc.home.dec.wx.vo.token.WeixinOauth2Token;
import com.fdc.home.dec.wx.vo.user.WeixinUserInfo;
import com.fdc.platform.common.yfutil.PropertyReader;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam; import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException; /**
* Created by pl on 2017/1/13.
*/
@Controller
public class OAuth2Controller { @Autowired
private DecWxService decWxService;
//判断用户是否登录的公用方法
CheckUserInfo checkUserInfo = new CheckUserInfo();
//从配置文件获取appid
public static String appid = PropertyReader.getValue("appid");
//从配置文件获取appsecret
public static String appsecret = PropertyReader.getValue("appsecret");
//从配置文件获取主域名
public static String indexUrl = PropertyReader.getValue("indexUrl");
public static String wtoken = PropertyReader.getValue("wholetoken"); /**
* 组装授权url
* @param request
* @param resultUrl
* @return
*/
@RequestMapping(value ="/oauth2Api")
public String oauth2API(HttpServletRequest request, @RequestParam String resultUrl) {
String redirectUrl = "";
if (resultUrl != null) {
String backUrl =indexUrl+"/oauth2MeUrl?oauth2url="+resultUrl;
//组装授权url
redirectUrl = WxUtils.oAuth2Url(appid, backUrl);
}
return "redirect:" + redirectUrl;
} /**
* 获取用户信息
* @param request
* @param response
* @param code
* @param oauth2url
* @return
* @throws IOException
*/
@RequestMapping(value = "/oauth2MeUrl")
public String oauth2MeUrl(HttpServletRequest request,HttpServletResponse response, @RequestParam String code, @RequestParam String oauth2url) throws IOException {
request.setCharacterEncoding("utf-8");
response.setCharacterEncoding("utf-8");
HttpSession session = request.getSession();
session.setAttribute("code",code);
// 用户同意授权
if (!"authdeny".equals(code)) {
// 获取网页授权access_token
WeixinOauth2Token weixinOauth2Token = WxUtils.getOauth2AccessToken(appid, appsecret, code);
// 网页授权接口访问凭证
String accessToken = weixinOauth2Token.getAccessToken();
// 用户标识
String openId = weixinOauth2Token.getOpenId();
String wholetoken = decWxService.getToken(wtoken);
//获取微信用户openid存储在cookie中的信息
Cookie userCookie=new Cookie("openId",openId);
userCookie.setMaxAge(-1);
userCookie.setPath("/");
response.addCookie(userCookie);
WeixinUserInfo weixinUserInfo = WxUtils.getWeixinUserInfo(wholetoken, openId);
//将用户信息写入redis
decWxService.setToken(openId, JSONHelper.beanToJson(weixinUserInfo));
}else {
return "redirect:"+indexUrl+"/error404";
}
return "redirect:" + oauth2url;
}
}

(注:WxUtils中封装各种请求微信服务器的接口,具体可自行百度)

以上两步基本可以解决用户授权的问题。基于此需要说明的是:

  • 开发中要设置cookie过期时间,设置为负数,表明当用户关闭浏览器的时候自动清空cookie,但在实际的测试中,微信浏览器并不会立刻清理cookie,你可以自行清理cookie.每次用户访问时直接从cookie中获取openid,若没有,才会调用微信接口获取。在获取openid的同时,更新redis缓存中的用户信息,这样达到及时同步用户信息的效果,也减少了对微信服务器的访问。
  • 有部分网页在博客中提到微信浏览器没有cookie和session,基于这类问题,还请自己动手验证吗,微信浏览器是有cookie和session的(请区分服务端session和客户端session),这里之所以没有将openid存储在session中,其主要是考虑到分布式多点项目中session比较难以处理。
  • 比较重要的一点是,当用户更换微信登录时,cookie会自动清除,登录成功后,会重新获取新登录的用户的openid。

结语

如果你还需要了解更多技术文章信息,请继续关注白衣秀才的博客
个人网站:http://penglei.top/
Github:https://github.com/whitescholars
微博:http://weibo.com/u/3034107691?refer_flag=1001030102_&is_all=1

微信--高效解决token及授权用户openid的持久化处理办法的更多相关文章

  1. ASP.NET Core2实现静默获取微信公众号的用户OpenId

    最近在做个微信公众号的项目,需要将入口放置在公众号二级菜单内,通过点击该菜单链接后进入到该项目中去,进入到项目后程序会自动通过微信公众号的API完成用户的OpenId获取.需求很简单,实现起来也不复杂 ...

  2. 解决微信公众号授权登录和开放平台微信第三方应用授权登录获取到的用户Openid关联问题

    开发背景: 最近一段时间一直在做关于微信方面的网站应用开发,这段时间也收获的不少关于微信开发方面的开发技能,接触的比较多的主要有微信公众号和微信网站app第三方登录授权,以及微信会员卡,优惠券和扫描二 ...

  3. 微信公众号订阅号以及服务号通过网页授权获取用户openid方法

    微信官方文档:https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140842 官方流程 网页授权流程分为四步: 1.引导用户 ...

  4. JAVA微信公众号网页开发 —— 用户授权获取openid

    官方文档:https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140842 HttpClientUtil.java packa ...

  5. 微信测试号开发之九 微信网页授权:页面获取用户openid

    原文链接:https://blog.csdn.net/qq_37936542/article/details/78981369 一:配置接口 注意:这里填写的是域名(是一个字符串),而不是URL,因此 ...

  6. 微信网页授权获取用户openid及用户信息

    $code = $_GET["code"];//获取code $appid=“xxxx”;//公众号appid $APPSECRET="xxx";//公众号ap ...

  7. 微信接口-获取用户openid基本信息

    一.协助获取微信用户openid功能 https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri= ...

  8. ***微信公众平台开发: 获取用户基本信息+OAuth2.0网页授权

    本文介绍如何获得微信公众平台关注用户的基本信息,包括昵称.头像.性别.国家.省份.城市.语言.本文的方法将囊括订阅号和服务号以及自定义菜单各种场景,无论是否有高级接口权限,都有办法来获得用户基本信息, ...

  9. 微信网页授权,获取微信code,获取access_tocken,获取用户信息

    微信开发中,经常有这样的需求:获得用户头像.绑定微信号给用户发信息.. 那么实现这些的前提就是授权!   1.配置安全回调域名: 在微信公众号请求用户网页授权之前,开发者需要先到公众平台官网中的“开发 ...

随机推荐

  1. NHibernate-Generator主键生成方式

    NHibernate之Generator主键生成方式   (1) assigned主键由外部程序负责生成,无需NHibernate参与. (2) hilo通过hi/lo 算法实现的主键生成机制,需要额 ...

  2. 快速构建Windows 8风格应用26-本地应用数据

    原文:快速构建Windows 8风格应用26-本地应用数据 本篇博文主要介绍如何获取应用的设置和文件容器.如何将数据写入设置.如何从设置中获取数据.如何删除设置中数据.如何将数据写入文件.如何从文件中 ...

  3. c语言内存对齐(1)

    在<C陷阱与缺陷>有这样一个例子: #include <stdio.h> int main(void) { int i; char c; ;i<;i++){ scanf( ...

  4. iOS_1_加法器

    : BeyondViewController.h // // BeyondViewController.h // 01_calc // // Created by beyond on 14-7-20. ...

  5. 02.零成本实现WEB性能测试-基于APACHE JMETER

    书评: 1.这本是介绍性能测试工具Jmeter的书籍,维度还够,但是粒度太粗. 2.对于想快速了解JMeter的使用和工具的原件使用,还是有一定的参考价值. 3.实际上,这本书可用来快速入门,掌握和了 ...

  6. java 权限 部分截图

    下载地址:http://download.csdn.net/detail/heyehuang/5857263

  7. Mysql 嵌套游标添以及任意位置声明变量的方法

    在写存储过程的时候,会遇到某个游标的筛选条件来自于 先前语句运行的结果,比较常见的方式是 再写一个存储过程,通过调用来完成 动态参数的配置, 或者使用 动态sql的功能,而这两种方式都不能很好的解决这 ...

  8. 《剑指Offer》面试题-用两个栈实现队列

    题目描述: 用两个栈来实现一个队列,完成队列的Push和Pop操作.队列中的元素为int类型. 输入: 每个输入文件包含一个测试样例.对于每个测试样例,第一行输入一个n(1<=n<=100 ...

  9. ORACLE表建立自增列

    create tablespace studentDBdatafile 'E:\datafiles_1.dbf' size 10m; create user Huang_Ying_Boidentifi ...

  10. How To Configure Logging And Log Rotation In Apache On An Ubuntu VPS

    Introduction The Apache web server can be configured to give the server administrator important info ...