又有一段时间没动笔了,确实工做后忙碌起来了,年底了,给你们拜年了,抽了这个空档把最近作的一个东西分享分享。html
微信公众号,相信很多人已经有接触,其开发分为许多种,本次主要说的是,经过微信认证后的公众号(下图,),使用Oauth网页接口来获取用户信息的流程。java
1、基础准备web
首先,明确需求:有一个应用服务器,但愿经过某个连接(菜单)进入此应用,在应用服务器端能够得到访问者的一些信息。json
微信是不会直接让你在用户在访问应用服务器时直接取到用户信息的,而须要经过他们的专门的认证服务器来操做。做为开发者,目前有两类获取用户信息的方法,这里只介绍Oauth 2.0认证(其它还有一种CGI接口,是必须加了微信公众号关注后才可取到用户信息,而使用Oauth则能够不加关注也能取到),结合微信我整理了一个简单的流程:api
一、用户点开了某个菜单或者连接,进入到了咱们的后台应用服务器(原始请求:URL_ORG);浏览器
二、服务器发现没有用户认证信息(微信所谓的认证code,使用一次后失效),因而返回了一个rediret头给用户,而且让它去访问微信的认证服务器;服务器
三、用户的浏览器会去访问微信的认证服务器(用户会看到闪一下,这是重定向进行中);微信
https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=URL_ORG&response_type=code&scope=snsapi_userinfo&state=1#wechat_redirect
四、若是用户受权(微信会询问,是否受权,若是加了关注的用户则不会询问,默认受权),则微信认证服务器会返回一个redirect头给用户,同时包含了认证code,让用户浏览器去访问原始请求的连接;session
五、这时用户的浏览器会带着code去访问咱们的后台应用(此时还会再闪一下,重定向:URL_ORG?code=xxxxxx);app
六、应用服务器拿到了code尚未用,还得由应用服务器去向微信认证服务器取token(ACCESS_TOKEN,一段时间内有效),在取得token时还会返回用户的openId(OEPNID);
https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code
七、在取到token、openId后,再发起一次请求,取得用户的我的信息;
https://api.weixin.qq.com/sns/userinfo?access_token=ACCESS_TOKEN&openid=OPENID&lang=zh_CN
上面的过程,看起来很是复杂, 没错,有两次重定向!可是好处在于,用户访问的原始连接确定是干净的,好比www.abc.com/xxx,微信那边的连接或菜单在设置时不须要变更。若是你们愿意去维护菜单的连接地址,也可使用微信官方文档的方法,省去上面的第一、2步,直接把原始连接设置为:微信认证接口地址+待回调原始地址,的模式,减小一次用户可感知的闪烁。
2、核心代码
转java后,我也没时间去写成其它语言了,你们就看java代码吧,第一个地方,对应用服务器需求使用用户信息的url地址使用filter:
1 public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { 2 HttpServletRequest request = (HttpServletRequest) servletRequest; 3 HttpServletResponse response = (HttpServletResponse) servletResponse; 4 HttpSession session = request.getSession(); 5 if (request != null) { 6 // 尝试公众号 7 Object openId = session.getAttribute(WxSession.WX_OPENID_KEY); 8 // 未完成认证 9 if (openId == null) { 10 String code = request.getParameter("code"); 11 if (code != null) { 12 // 第二步,经过code换取access_token和OpenId 13 JSONObject ret = _authService.getAccessToken(code); 14 String token = ret.getString("access_token"); 15 openId = ret.getString("openid"); 16 // 第三步,拉取用户信息 17 ret = _authService.getExternalUserInfo(token, openId.toString()); 18 String nickname = ret.getString("nickname"); 19 // 存session 20 session.setAttribute(WxSession.WX_OPENID_KEY, openId); 21 session.setAttribute(WxSession.WX_USER_NAME_KEY, nickname); 22 } else { 23 // 第一步,获取code 24 response.sendRedirect(_authService.getExternalAuthUrl(request.getRequestURL().toString())); 25 return; 26 } 27 } 28 } 29 filterChain.doFilter(servletRequest, servletResponse); 30 }
上面的代码有注释,很是明白了,先看有没有code,没有code就重定向去取,取到后用户再来;当有code时,服务器本身去取token和openid,取到后再取任何用户信息(上面只取了用户昵称)。
第二个地方,上面代码中的getExternalAuthUrl、getAccessToken、getExternalUserInfo三个方法:
1 /** 2 * 跳转到OAuth登陆页面,可有用户的详细信息 3 */ 4 public String getExternalAuthUrl(String redirectUri) { 5 String encodeUrl = null; 6 try { 7 encodeUrl = URLEncoder.encode(redirectUri, "UTF-8"); 8 } catch (UnsupportedEncodingException e) { 9 _log.error(e); 10 } 11 String url = "https://open.weixin.qq.com/connect/oauth2/authorize"; 12 url += "?appid=" + Constant.Subscribe.AppId; 13 url += "&redirect_uri=" + encodeUrl; 14 url += "&response_type=code"; 15 url += "&scope=snsapi_userinfo"; 16 url += "&state=123"; 17 url += "#wechat_redirect"; 18 return url; 19 } 20 21 /** 22 * 获取OAuth的access_token,错误时返回null 23 */ 24 public JSONObject getAccessToken(String code) { 25 String url = "https://api.weixin.qq.com/sns/oauth2/access_token"; 26 url += "?appid=" + Constant.Subscribe.AppId; 27 url += "&secret=" + Constant.Subscribe.AppSecret; 28 url += "&code=" + code; 29 url += "&grant_type=authorization_code"; 30 try { 31 String responseStr = HttpInvork.getRequestWithoutHeader(url); 32 JSONObject json = JSONObject.fromObject(responseStr); 33 String access_token = json.getString("access_token"); 34 if (StringUtils.isNotEmpty(access_token)) { 35 return json; 36 } 37 } catch (Exception e) { 38 _log.error(e); 39 } 40 return null; 41 } 42 43 /** 44 * 获取OAuth微信用户信息,错误时返回null 45 */ 46 public JSONObject getExternalUserInfo(String accessToken, String openId) { 47 String url = "https://api.weixin.qq.com/sns/userinfo"; 48 url += "?access_token=" + accessToken; 49 url += "&openid=" + openId; 50 url += "&lang=zh_CN"; 51 String responseStr = HttpInvork.getRequestWithoutHeader(url); 52 try { 53 return JSONObject.fromObject(responseStr); 54 } catch (Exception e) { 55 _log.error(e); 56 } 57 return null; 58 }
第三个地方,上面的HttpInvork.getRequestWithoutHeader方法,我就不贴了,你们随便写/找个能够发送get请求的方法代替一下吧。
3、福利?
你们不知足于Oauth?那我把CGI代码也贴出来吧。先仍是说明一下:使用cgi方式时,openid仍是跟前面的相似,scope可改成snsapi_base(不改也无所谓啦,还有一种openid获取方法前面没有说,就是向公众号发了消息,而后经过微信的公众平台的服务器配置绑定到了后台应用,经过解析后也能够取到openid),而后仍是要token才能要用户信息,只是这里就不能用Oauth的token了,而是普通的cgi token,代码:
1 /** 2 * 获取CGI的access_token,错误时返回null 3 */ 4 public String getCgiAccessToken() { 5 String url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential"; 6 url += "&appid=" + Constant.Subscribe.AppId; 7 url += "&secret=" + Constant.Subscribe.AppSecret; 8 try { 9 String responseStr = HttpInvork.getRequestWithoutHeader(url); 10 JSONObject json = JSONObject.fromObject(responseStr); 11 String access_token = json.getString("access_token"); 12 if (StringUtils.isNotEmpty(access_token)) { 13 return access_token; 14 } 15 } catch (Exception e) { 16 _log.error(e); 17 } 18 return null; 19 } 20 21 /** 22 * 经过CGI获取微信用户信息,错误时返回null 23 */ 24 public JSONObject getCgiUserInfo(String accessToken, String openId) { 25 String url = "https://api.weixin.qq.com/cgi-bin/user/info"; 26 url += "?access_token=" + accessToken; 27 url += "&openid=" + openId; 28 url += "&lang=zh_CN"; 29 String responseStr = HttpInvork.getRequestWithoutHeader(url); 30 try { 31 return JSONObject.fromObject(responseStr); 32 } catch (Exception e) { 33 _log.error(e); 34 } 35 return null; 36 }
说了好多,我建议是,你们申请一个实战演练演练,除了认证复杂了点,其它开发跟传统的web开发没啥太大区别了,微信JS-SDK都给你准备好了。过年快乐!