摘要

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

一:如何存储获取用户信息及调用第三方接口所需要的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

拦截器:

  1. package com.fdc.home.dec.wx.filter;
  2.  
  3. import com.alibaba.dubbo.common.utils.StringUtils;
  4. import com.fdc.platform.common.yfutil.PropertyReader;
  5. import org.springframework.web.method.HandlerMethod;
  6. import org.springframework.web.servlet.ModelAndView;
  7. import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
  8.  
  9. import javax.servlet.http.Cookie;
  10. import javax.servlet.http.HttpServletRequest;
  11. import javax.servlet.http.HttpServletResponse;
  12. import java.util.Arrays;
  13.  
  14. /**
  15. * Created by pl on 2017/1/13.
  16. */
  17. public class OAuth2Interceptor extends HandlerInterceptorAdapter {
  18.  
  19. public static String indexUrl = PropertyReader.getValue("indexUrl");//从配置文件中读取域名
  20. // private static String[] arrQueController = {"newquestion", "mycenter","testCookie"};
  21. public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
  22. throws Exception {
  23. Cookie[] cookies = request.getCookies();
  24. String openId=null;
  25. //判断cookie中是否存在openid 若存在则直接跳过,不存在则获取一次
  26. if(cookies!=null){
  27. for(Cookie cookie : cookies){
  28. if(cookie.getName().equals("openId")){
  29. openId = cookie.getValue();
  30. }
  31. }
  32. }
  33. if (StringUtils.isEmpty(openId)) {
  34. if (handler instanceof HandlerMethod) {
  35. HandlerMethod handlerMethod = (HandlerMethod) handler;
  36. String methodName = handlerMethod.getMethod().getName();
  37. String uri = request.getRequestURI();
  38. // if (checkList(arrQueController, methodName)) {
  39. // System.out.println("执行了");
  40. response.sendRedirect(indexUrl + "/oauth2Api?resultUrl=" + indexUrl + uri);
  41. return false;
  42. // }
  43. }
  44. return true;
  45. } else {
  46. return true;
  47. }
  48. }
  49.  
  50. public void postHandle(
  51. HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)
  52. throws Exception {
  53. }
  54.  
  55. public void afterCompletion(
  56. HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
  57. throws Exception {
  58. }
  59.  
  60. public boolean checkList(String[] arr, String targetValue) {
  61. return Arrays.asList(arr).contains(targetValue);
  62. }
  63.  
  64. }

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

  1. <mvc:interceptors>
  2. <mvc:interceptor>
  3. <mvc:mapping path="/testCookie"/>
  4. <mvc:mapping path="/testCookie1"/>
  5. <bean class="com.fdc.home.dec.wx.filter.OAuth2Interceptor"/>
  6. </mvc:interceptor>
  7. </mvc:interceptors>

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

  1. package com.fdc.home.dec.wx.controller;
  2.  
  3. import com.fdc.home.dec.service.inter.service.DecWxService;
  4. import com.fdc.home.dec.wx.service.CheckUserInfo;
  5. import com.fdc.home.dec.wx.utils.JSONHelper;
  6. import com.fdc.home.dec.wx.utils.WxUtils;
  7. import com.fdc.home.dec.wx.vo.token.WeixinOauth2Token;
  8. import com.fdc.home.dec.wx.vo.user.WeixinUserInfo;
  9. import com.fdc.platform.common.yfutil.PropertyReader;
  10. import org.springframework.beans.factory.annotation.Autowired;
  11. import org.springframework.stereotype.Controller;
  12. import org.springframework.web.bind.annotation.RequestMapping;
  13. import org.springframework.web.bind.annotation.RequestParam;
  14.  
  15. import javax.servlet.http.Cookie;
  16. import javax.servlet.http.HttpServletRequest;
  17. import javax.servlet.http.HttpServletResponse;
  18. import javax.servlet.http.HttpSession;
  19. import java.io.IOException;
  20.  
  21. /**
  22. * Created by pl on 2017/1/13.
  23. */
  24. @Controller
  25. public class OAuth2Controller {
  26.  
  27. @Autowired
  28. private DecWxService decWxService;
  29. //判断用户是否登录的公用方法
  30. CheckUserInfo checkUserInfo = new CheckUserInfo();
  31. //从配置文件获取appid
  32. public static String appid = PropertyReader.getValue("appid");
  33. //从配置文件获取appsecret
  34. public static String appsecret = PropertyReader.getValue("appsecret");
  35. //从配置文件获取主域名
  36. public static String indexUrl = PropertyReader.getValue("indexUrl");
  37. public static String wtoken = PropertyReader.getValue("wholetoken");
  38.  
  39. /**
  40. * 组装授权url
  41. * @param request
  42. * @param resultUrl
  43. * @return
  44. */
  45. @RequestMapping(value ="/oauth2Api")
  46. public String oauth2API(HttpServletRequest request, @RequestParam String resultUrl) {
  47. String redirectUrl = "";
  48. if (resultUrl != null) {
  49. String backUrl =indexUrl+"/oauth2MeUrl?oauth2url="+resultUrl;
  50. //组装授权url
  51. redirectUrl = WxUtils.oAuth2Url(appid, backUrl);
  52. }
  53. return "redirect:" + redirectUrl;
  54. }
  55.  
  56. /**
  57. * 获取用户信息
  58. * @param request
  59. * @param response
  60. * @param code
  61. * @param oauth2url
  62. * @return
  63. * @throws IOException
  64. */
  65. @RequestMapping(value = "/oauth2MeUrl")
  66. public String oauth2MeUrl(HttpServletRequest request,HttpServletResponse response, @RequestParam String code, @RequestParam String oauth2url) throws IOException {
  67. request.setCharacterEncoding("utf-8");
  68. response.setCharacterEncoding("utf-8");
  69. HttpSession session = request.getSession();
  70. session.setAttribute("code",code);
  71. // 用户同意授权
  72. if (!"authdeny".equals(code)) {
  73. // 获取网页授权access_token
  74. WeixinOauth2Token weixinOauth2Token = WxUtils.getOauth2AccessToken(appid, appsecret, code);
  75. // 网页授权接口访问凭证
  76. String accessToken = weixinOauth2Token.getAccessToken();
  77. // 用户标识
  78. String openId = weixinOauth2Token.getOpenId();
  79. String wholetoken = decWxService.getToken(wtoken);
  80. //获取微信用户openid存储在cookie中的信息
  81. Cookie userCookie=new Cookie("openId",openId);
  82. userCookie.setMaxAge(-1);
  83. userCookie.setPath("/");
  84. response.addCookie(userCookie);
  85. WeixinUserInfo weixinUserInfo = WxUtils.getWeixinUserInfo(wholetoken, openId);
  86. //将用户信息写入redis
  87. decWxService.setToken(openId, JSONHelper.beanToJson(weixinUserInfo));
  88. }else {
  89. return "redirect:"+indexUrl+"/error404";
  90. }
  91. return "redirect:" + oauth2url;
  92. }
  93. }

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

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

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

微信 openId的更多相关文章

  1. 获取微信openID 的步骤

    获取微信openid的步骤:1.进入-->判断openID是否为空: 空-->$url=urlencode("http://xxx/xxx.php");//回调链接 $ ...

  2. 微信OPENID授权方法

    今天搞了下微信授权, 总结了下微信的授权规则与步骤 先来几个关键字 Openid  微信ip(属于唯一指向公众号的id) redirect_uri  授权回调地址 State 回调地址带参数 Appi ...

  3. 微信公众号开发系列-获取微信OpenID

    在微信开发时候在做消息接口交互的时候须要使用带微信加密ID(OpenId),下面讲讲述2中类型方式获取微信OpenID.接收事件推送方式和网页授权获取用户基本信息方式获取. 1.通过接收被动消息方式获 ...

  4. ASP.NET CORE下用盛派微信SDK取微信openid

    用CORE做项目用到微信的相关东西,听说那个盛派微信SDK很火,自己弄了下,只是简单的用用,用户访问页面取微信openid

  5. 防止活动上线时 微信openid 被伪造的解决办法

    背景 前不久上线了一个 campaign 项目,一个 h5,后端为php,用户可以在微信中通过网页授权的方式登录,然后用微信 openid 作为唯一标识符进行签到和抽奖的操作. 结果后期出现了很多脏数 ...

  6. 微信openid

    微信openid由用户id和公众号id加密而来,同一用户相对同一公众账号的openid是不变的.

  7. php获取微信openid

    使用微信接口,无论是自动登录还是微信支付我们首先需要获取的就是openid,获取openid的方式有两种,一种是在关注的时候进行获取,这种订阅号就可以获取的到,第二种是通过网页授权获取,这种获取需要的 ...

  8. 微信openid获取(php),

    在看这个的基础上有阅读过微信的相关文档, 这段url是给用户的,当用户点击进去后会出一个,确认登录授权,需要用户点击授权之后跳到你的授权回调地址(注意:下面php代码必须放在授权回调地址中:比如回调地 ...

  9. 通过php获取用户微信openid

    // 基于CI框架 // 访问开始页面 public function url() { // wxAction/oauth2 微信回调地址:微信传入code值,通过该code在wxAction/oau ...

  10. 微信OpenID获取

    用户要求在微信端登录一次后,以后不需要再登录.  我的系统是单独的一个网站. 使用MVC的记住密码功能, 如果用户重启,就还是要输入密码,所以需要有一个唯一不变的用来标示用户的ID.  OpenID就 ...

随机推荐

  1. 我装win8与win7双系统的血泪史

    前段时间教徒弟装系统,由于笔记本原带了win8,他不想换掉原来的系统.遂决定装个双系统.于是按照之前的一贯套路,但是出现了问题. 一. 首先遇到的问题是:如何进入BIOS,设置成U盘启动.Win XP ...

  2. webDriver API——第14部分Color Support

    class selenium.webdriver.support.color.Color(red, green, blue, alpha=1) Bases: object Color conversi ...

  3. [LeetCode] Add Two Numbers(stored in List)

    首先,演示一个错误的reverList class Solution { public: ListNode* reverse(ListNode* root) { if(NULL == root) re ...

  4. iOS-使用添加的花样字体

    代码地址如下:http://www.demodashi.com/demo/11501.html 项目需求中, 有时候有些金额利率等这些不用系统默认字体展现, 而需要着重突出展示! 一.项目截图及效果截 ...

  5. MQTT---HiveMQ源代码具体解释(七)Netty-SSL/NoSSL

    源博客地址:http://blog.csdn.net/pipinet123 MQTT交流群:221405150 实现功能 依据用户配置的不同的Listener(TcpListener.TlsTcpLi ...

  6. vs2012 提示 未能正确加载 "Visual C++ Language Manager Package" 包

    1.点击vs2012菜单栏 工具-> Visual Studio 命令提示 打开命令窗口 2.输入命令 "devenv /Setup" 3.重新打开vs2012 via: h ...

  7. STL容器分析--list

    就是一双向链表,可高效地进行插入删除元素.

  8. Unity3D-rigidBody.velocity

    还有半小时就下班了.写一下今天遇到的问题.处理方法以及一些自己的理解.理解的不一定对,还希望大家指正. 今天我做的效果是,hero的移动. 曾经做过用的是transform.Translate(Vec ...

  9. MySQL之desc查看表结构的详细信息

    在mysql中如果想要查看表的定义的话:有如下方式可供选择 1.show create table 语句: show create table table_name; 2.desc table_nam ...

  10. 使用 P3P 规范让 IE 跨域接受第三方 cookie

    前两天帮同事处理一个 js 跨域问题,使用 jsonp 跨域提交用户名密码请求,实现自动登录第三方网站,即 SSO(single-sign-on) 单点登录,一处登录处处登录.在 Chrome 下没问 ...