微信--高效解决token及授权用户openid的持久化处理办法
关于微信开发的话题,例子确实已经有不少,但大部分都是人云亦云,很多小细节或者需要注意的地方却大多没有讲清楚,这令很多刚开始开发的人感觉大很迷茫。而我今天要说的话题,主要着眼于两个方面。
一:如何存储获取用户信息及调用第三方接口所需要的token.
二 : 第三方页面授权,如何减少从微信服务器获取用户openid的次数以及减少获取用户信息的次数,加速第三方页面的加载速速。
(注:演示所使用的是java语言,其他语言可与此类似)
下面我将开始讲述第一个问题。
如何存储获取用户信息及调用第三方接口所需要的token?
从微信的官方文档上,我们知道,获取token的次数为1天2000次,每两小时token失效一次,对于那种一天没有几个人访问的微信公众号而言,他们可能只是简单的每次调用高级接口都会去获取一遍token.众所周知,这种做法极其的耗费时间。从网上其他网友给出的存储方案大概有如下几种:
- 数据库:通过微信接口获取到 Token 之后,将 Token存储到数据库,每次需要时从数据库取出。采用定时任务的方法每隔一个固定的时间去获取一次token.
- NoSQl:这里以 Redis 为例子。通过微信接口获取到 Token 之后,存入 Redis,可以通过设置redis的过期时间,每次需要token时从redis中取出来,若没有,则证明Token 已过期可重新获取(当然也可采用上面的定时任务的方式定期获取)。
- 文件存储:这个比较适合单一公众号的情况。通过微信接口获取到 Token 之后,存入文件,采用定时任务的方法每隔一个固定的时间去获取一次token.
第三方页面授权,如何减少从微信服务器获取用户openid的次数以及减少获取用户信息的次数,加速第三方页面的加载速速?
从诸多的博客中,我们了解到,第三方页面授权获取用户信息,我们要调用两次微信接口。
- 第一次:构造应用授权的url,通过返回的code,换取用户的openid.
- 第二次:通过用户的openid与token获取用户信息。
对于页面比较多的应用,每个页面请求时都需要调用两个方法,于用户而言这是极其耗费时间的。
然而诸多的博客只是告诉我们改如何处理用户信息,诸如将用户信息先拉取下来存储到自己的数据库,然后每次需要时从自己的数据库中通过openid来获取。殊不知这种博客只说了一点,他们没有考虑到的问题是:
- 用户若修改了自己的信息,该如何同步到自己的表里面.
- 用户的信息是获取到了,但是每次用户访问网页,标识用户身份的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的持久化处理办法的更多相关文章
- ASP.NET Core2实现静默获取微信公众号的用户OpenId
最近在做个微信公众号的项目,需要将入口放置在公众号二级菜单内,通过点击该菜单链接后进入到该项目中去,进入到项目后程序会自动通过微信公众号的API完成用户的OpenId获取.需求很简单,实现起来也不复杂 ...
- 解决微信公众号授权登录和开放平台微信第三方应用授权登录获取到的用户Openid关联问题
开发背景: 最近一段时间一直在做关于微信方面的网站应用开发,这段时间也收获的不少关于微信开发方面的开发技能,接触的比较多的主要有微信公众号和微信网站app第三方登录授权,以及微信会员卡,优惠券和扫描二 ...
- 微信公众号订阅号以及服务号通过网页授权获取用户openid方法
微信官方文档:https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140842 官方流程 网页授权流程分为四步: 1.引导用户 ...
- JAVA微信公众号网页开发 —— 用户授权获取openid
官方文档:https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140842 HttpClientUtil.java packa ...
- 微信测试号开发之九 微信网页授权:页面获取用户openid
原文链接:https://blog.csdn.net/qq_37936542/article/details/78981369 一:配置接口 注意:这里填写的是域名(是一个字符串),而不是URL,因此 ...
- 微信网页授权获取用户openid及用户信息
$code = $_GET["code"];//获取code $appid=“xxxx”;//公众号appid $APPSECRET="xxx";//公众号ap ...
- 微信接口-获取用户openid基本信息
一.协助获取微信用户openid功能 https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri= ...
- ***微信公众平台开发: 获取用户基本信息+OAuth2.0网页授权
本文介绍如何获得微信公众平台关注用户的基本信息,包括昵称.头像.性别.国家.省份.城市.语言.本文的方法将囊括订阅号和服务号以及自定义菜单各种场景,无论是否有高级接口权限,都有办法来获得用户基本信息, ...
- 微信网页授权,获取微信code,获取access_tocken,获取用户信息
微信开发中,经常有这样的需求:获得用户头像.绑定微信号给用户发信息.. 那么实现这些的前提就是授权! 1.配置安全回调域名: 在微信公众号请求用户网页授权之前,开发者需要先到公众平台官网中的“开发 ...
随机推荐
- SQL点滴16—SQL分页语句总结
原文:SQL点滴16-SQL分页语句总结 今天对分页语句做一个简单的总结,他们大同小异的,只要理解其中一个其他的就很好理解了. 使用top选项 *from Orders orderid from Or ...
- OCP-1Z0-051-题目解析-第11题
11. View the Exhibit and examine the structure of the PRODUCTS table.All products have a list price. ...
- 多线程下HashMap的死循环是如何产生的
前言 HashMap不是线程安全的,如果需要在多线程环境中使用Map,那么我们可以使用ConcurrentHashmap. 1.举例说明: package com.test; import java. ...
- 1001 - Another A+B
1001 - Another A+B Description Give you an integer a, you are to find two another integers which sum ...
- 通过改变viewport 实现网站自适应
var phoneWidth = parseInt(window.screen.width); var phoneScale = phoneWidth/640; var userAgent = nav ...
- 如何防范CC攻击
服务器如何防范CC攻击CC攻击是DDOS(分布式拒绝服务)的一种,相比其它的DDOS攻击CC似乎更有技术含量一些.这种攻击你见不到虚假IP,见不到特别大的异常流量,但造成服务器无法进行正常连接,听说一 ...
- Windows Live Writer 完成开源并推出开源分支
原文:Announcing Open Live Writer - An Open Source Fork of Windows Live Writer Windows Live Writer是一款发布 ...
- PRML 第三章 - 线性回归
这段时间组里在有计划地学习书籍PRML (Pattern Recognition and Machine Learning),前两天自己做了一个里面第三章linear regression的分享,这里 ...
- 读写ini文件
C# 使用文件流来读写ini文件 背景 之前采用ini文件作为程序的配置文件,觉得这种结构简单明了,配置起来也挺方便.然后操作方式是通过WindowsAPI,然后再网上找到一个基于WindowsAPI ...
- Linux网络编程(六)
网络编程中,使用多路IO复用的典型场合: 1.当客户处理多个描述字时(交互式输入以及网络接口),必须使用IO复用. 2.一个客户同时处理多个套接口. 3.一个tcp服务程序既要处理监听套接口,又要处理 ...