公众号openid实时存储mysql_微信--高效解决token及授权用户openid的持久化处理办法...
摘要
关于微信开发的话题,例子确实已经有不少,但大部分都是人云亦云,很多小细节或者需要注意的地方却大多没有讲清楚,这令很多刚开始开发的人感觉大很迷茫。而我今天要说的话题,主要着眼于两个方面。
一:如何存储获取用户信息及调用第三方接口所需要的token.
二 : 第三方页面授权,如何减少从微信服务器获取用户openid的次数以及减少获取用户信息的次数,加速第三方页面的加载速速。
(注:演示所使用的是java语言,其他语言可与此类似)
下面我将开始讲述第一个问题。
如何存储获取用户信息及调用第三方接口所需要的token?
从微信的官方文档上,我们知道,获取token的次数为1天2000次,每两小时token失效一次,对于那种一天没有几个人访问的微信公众号而言,他们可能只是简单的每次调用高级接口都会去获取一遍token.众所周知,这种做法极其的耗费时间。从网上其他网友给出的存储方案大概有如下几种:
数据库:通过微信接口获取到 Token 之后,将 Token存储到数据库,每次需要时从数据库取出。采用定时任务的方法每隔一个固定的时间去获取一次token.
NoSQl:这里以 Redis 为例子。通过微信接口获取到 Token 之后,存入 Redis,可以通过设置redis的过期时间,每次需要token时从redis中取出来,若没有,则证明Token 已过期可重新获取(当然也可采用上面的定时任务的方式定期获取)。
文件存储:这个比较适合单一公众号的情况。通过微信接口获取到 Token 之后,存入文件,采用定时任务的方法每隔一个固定的时间去获取一次token.
(固定的时间:一般设为1小时为宜,如1小时后因网络原因,请求获取token失败,则原有的token还可以在使用1小时,这种方式将错误降低了一半)
大致有这三种方案。当然对于那些将token存储在session或者cookie里面的,这里我只能呵呵一笑了。
基于以上三种方式,我个人比较推崇的一个顺序是NoSQl>数据库>文件存储。
下面我分别来说说他们的优缺点:
采用数据库和文件存储,对于单节点并且用户请求数不是很多的web项目而言,是可以的正常运行的,但是对于分布式多节点的项目,采用这两种方式是行不通的。而我今天要说的第三种通过Redis方法存储,一方面它可以提升获取token的速度,另一方面当分布式项目的多个节点要公用同一个token的时候,我们可以方便的取到。
第三方页面授权,如何减少从微信服务器获取用户openid的次数以及减少获取用户信息的次数,加速第三方页面的加载速速?
从诸多的博客中,我们了解到,第三方页面授权获取用户信息,我们要调用两次微信接口。
第一次:构造应用授权的url,通过返回的code,换取用户的openid.
第二次:通过用户的openid与token获取用户信息。
对于页面比较多的应用,每个页面请求时都需要调用两个方法,于用户而言这是极其耗费时间的。
然而诸多的博客只是告诉我们改如何处理用户信息,诸如将用户信息先拉取下来存储到自己的数据库,然后每次需要时从自己的数据库中通过openid来获取。殊不知这种博客只说了一点,他们没有考虑到的问题是:
用户若修改了自己的信息,该如何同步到自己的表里面.
用户的信息是获取到了,但是每次用户访问网页,标识用户身份的openid依旧每次都要去调用接口获取。(获取用户信息的次数微信API规定为500000次)
那么接下来我要说的这个就是如何解决上面两个问题,处理过程大致如下:
a.添加拦截器,拦截需要授权页面的controller
拦截器:
packagecom.fdc.home.dec.wx.filter;
importcom.alibaba.dubbo.common.utils.StringUtils;importcom.fdc.platform.common.yfutil.PropertyReader;importorg.springframework.web.method.HandlerMethod;importorg.springframework.web.servlet.ModelAndView;importorg.springframework.web.servlet.handler.HandlerInterceptorAdapter;
importjavax.servlet.http.Cookie;importjavax.servlet.http.HttpServletRequest;importjavax.servlet.http.HttpServletResponse;importjava.util.Arrays;
/**
Created by pl on 2017/1/13.
*/
public class OAuth2Interceptor extendsHandlerInterceptorAdapter {
public static String indexUrl = PropertyReader.getValue("indexUrl");//从配置文件中读取域名//private static String[] arrQueController = {"newquestion", "mycenter","testCookie"};
public booleanpreHandle(HttpServletRequest request, HttpServletResponse response, Object handler)throwsException {
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 instanceofHandlerMethod) {
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 voidpostHandle(
HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)throwsException {
}
public voidafterCompletion(
HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)throwsException {
}
public booleancheckList(String[] arr, String targetValue) {returnArrays.asList(arr).contains(targetValue);
}
}
spring-servlet.xml (拦截两个指定的controller:testCookie,testCookie1)
b.用户首次访问,调用微信接口获取openid和用户信息。将openid写入服务器端的cookie,将用户的信息写入redis缓存中,以openid作为redis的key。
packagecom.fdc.home.dec.wx.controller;
importcom.fdc.home.dec.service.inter.service.DecWxService;importcom.fdc.home.dec.wx.service.CheckUserInfo;importcom.fdc.home.dec.wx.utils.JSONHelper;importcom.fdc.home.dec.wx.utils.WxUtils;importcom.fdc.home.dec.wx.vo.token.WeixinOauth2Token;importcom.fdc.home.dec.wx.vo.user.WeixinUserInfo;importcom.fdc.platform.common.yfutil.PropertyReader;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.stereotype.Controller;importorg.springframework.web.bind.annotation.RequestMapping;importorg.springframework.web.bind.annotation.RequestParam;
importjavax.servlet.http.Cookie;importjavax.servlet.http.HttpServletRequest;importjavax.servlet.http.HttpServletResponse;importjavax.servlet.http.HttpSession;importjava.io.IOException;
/**
Created by pl on 2017/1/13.
*/@Controllerpublic classOAuth2Controller {
@Autowired
privateDecWxService decWxService;//判断用户是否登录的公用方法
CheckUserInfo checkUserInfo = newCheckUserInfo();//从配置文件获取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
@paramrequest
@paramresultUrl
@return
*/@RequestMapping(value="/oauth2Api")publicString 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;
}
/**
获取用户信息
@paramrequest
@paramresponse
@paramcode
@paramoauth2url
@return
@throwsIOException*/@RequestMapping(value= "/oauth2MeUrl")public String oauth2MeUrl(HttpServletRequest request,HttpServletResponse response, @RequestParam String code, @RequestParam String oauth2url) throwsIOException {
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。
公众号openid实时存储mysql_微信--高效解决token及授权用户openid的持久化处理办法...相关推荐
- 微信公众号开发教程(六)获取微信用户信息-网页授权
作者:陈惠,叩丁狼教育高级讲师.原创文章,转载请注明出处. 在学习网页授权之前,我们先来了解下这次的需求: 我们的应用中有一个用来显示个人信息的页面,当微信用户在微信app中打开这个页面,希望可以获取 ...
- url 微信公众号开发 配置失效_微信公众号开发之授权登录
一.UnionId和openId 微信登录最重要的两个返回信息,一个是UnionId,一个是OpenId.两者之间有着必然的联系. UnionID机制的作用说明:如果开发者拥有多个移动应用.网站应用和 ...
- php微信公众号支付实例教程,php微信支付之公众号支付功能
这篇文章主要为大家详细介绍了php微信支付之公众号支付功能,具有一定的参考价值,感兴趣的小伙伴们可以参考一下 网上的很多PHP微信扫码支付接入教程都颇为复杂,且需要配置和引入较多的文件,本人通过整理后 ...
- 【微信公众号发红包转账】微信公众号上手机网页接收请求,通过公众号给用户发红包 开发流程...
有了微信支付 的开发做铺垫,相关的微信其他业务处理起来逻辑就能清晰很多. 准备好这两个架包 ------------------------------------------------------ ...
- 关于微信公众号页面获取code进行微信授权登录
关于微信公众号页面获取code进行微信授权登录 前言 提示:本文章为个人平时开发中的一些坑,对于新手可以耐心看一下,如果您感觉可以的话,请点个小星星再走吧,多谢了~~~ 提示:以下是本篇文章正文内容( ...
- 分享公众号抽奖的作用_微信公众号抽奖活动怎么弄
公众号线上抽奖相比于现场抽奖有很多好处,除了成本更低,宣传范围更广,能够吸引更多的参与,还有一大好处就是,线上抽奖比线下更容易挖掘潜在客户.抽奖活动可以手机客户的手机号,年龄,消费信息等.对流量转化和 ...
- java微信公众号面试题_使用微信公众号开发模拟面试功能
最近在整理我在大厂面试以及平时工作中遇到的问题,记录在 shfshanyue/Daily-Question 中,但觉得对于时时回顾,常常复习仍然做的不够. 于是在微信公众号中开发了随机生成模拟面试的功 ...
- 【微信公众号开发】八、微信JS发起支付
重要声明:本文章仅仅代表了作者个人对此观点的理解和表述.读者请查阅时持自己的意见进行讨论. 目录 本系列博文还包含了下面的博客: [微信公众号开发]一.运作及配置流程简介 [微信公众号开发]二.解析微 ...
- 微信(jspai版本即公众号h5版)支付-微信下单支付及企业转账到零钱
微信(jspai版本即公众号h5版)支付-微信下单支付及企业转账到零钱 一.后端前置条件 二.配置h5 devServer 三.后端开发代码 四.前端开发代码(uniapp) 一.后端前置条件 准备好 ...
最新文章
- 浏览器控制台执行代码_JavaScript 和 浏览器那些事
- Mysql导入数据时-data truncated for column..
- diy一个android手机版下载,原神个人自制版
- 分部类和方法的学习以及在LINQ中的应用(原创)
- MySQL 获取物理表的主键字段
- 计算机端口详解(总结)
- .gpx文件转geojson
- chrome清除缓存快捷键
- 从数字艺术品到 NFT
- 小白新手web开发简单总结(三)-一个简单的Web开发项目
- 谷歌gmail注册入口_Gmail,日历和其他Google Apps即将出现的外观如下
- 使用pynput监听键盘组合键
- Mybatis-There is no getter for property named 'tj' in 'class
- Linux网络容灾,关于异地容灾的感触
- 如何真正做好客户管理
- 西门子TIA博途仿真器PLCSIM使用教程
- 关于网页中显示生僻字的方法
- 局部搜索、模拟退火和遗传算法求解TSP问题
- 关于有刷直流电机,你需要了解这些
- 移动端 简易的table表格
热门文章
- Git应用之eclipse解决冲突代码
- C语言为什么被const声明的变量不是一个常量表达式
- MRC与ARC混合编程的编译器标记
- 剑指offer——圆圈中最后剩下的数字
- IE6 透明遮挡falsh解决方案
- SAP NetWeaver 平台介绍
- 用SSAS将多个FLV和MP3合成一个FLV文件
- 为什么php md5,为什么php md5()总是与python的不同哈希.md5()如果使用汉字?
- 炉石整活拔线方法_酒馆战棋:整活如何简单“拔线”?瓦娘在线教学,却3本得死神?...
- 02:陶陶摘苹果【一维数组】