微信公众平台开发教程(三)_OAuth2.0认证
一.微信授权认证
如果用户在微信客户端中访问第三方网页,公众号可以通过微信网页授权机制,来获取用户基本信息,进而实现业务逻辑。
关于网页授权的两种scope的区别说明
1、以snsapi_base为scope发起的网页授权,是用来获取进入页面的用户的openid的,并且是静默授权并自动跳转到回调页的。用户感知的就是直接进入了回调页(往往是业务页面)
2、以snsapi_userinfo为scope发起的网页授权,是用来获取用户的基本信息的。但这种授权需要用户手动同意,并且由于用户同意过,所以无须关注,就可在授权后获取该用户的基本信息。
3、用户管理类接口中的“获取用户基本信息接口”,是在用户和公众号产生消息交互或关注后事件推送后,才能根据用户OpenID来获取用户基本信息。这个接口,包括其他微信接口,都是需要该用户(即openid)关注了公众号后,才能调用成功的。
关于网页授权access_token和普通access_token的区别
1、微信网页授权是通过OAuth2.0机制实现的,在用户授权给公众号后,公众号可以获取到一个网页授权特有的接口调用凭证(网页授权access_token),通过网页授权access_token可以进行授权后接口调用,如获取用户基本信息;
2、其他微信接口,需要通过基础支持中的“获取access_token”接口来获取到的普通access_token调用。
关于特殊场景下的静默授权
1、上面已经提到,对于以snsapi_base为scope的网页授权,就静默授权的,用户无感知;
2、对于已关注公众号的用户,如果用户从公众号的会话或者自定义菜单进入本公众号的网页授权页,即使是scope为snsapi_userinfo,也是静默授权,用户无感知。
具体而言,网页授权流程分为四步:
1、引导用户进入授权页面同意授权,获取code
2、通过code换取网页授权access_token(与基础支持中的access_token不同)
3、如果需要,开发者可以刷新网页授权access_token,避免过期
4、通过网页授权access_token和openid获取用户基本信息(支持UnionID机制)
以上摘自官方开发文档。点击这里查看。
个人理解就是一旦用户在微信公众平台上进行第三方页面跳转,均会触发认证。如在公众号内通过点击菜单跳转网页(此情况一般在创建菜单对应的url就会引导如下地址)或者是
在点击图文消息时(需要设置对应的url)。
此时开发者需要引导用户打开如下地址来获取code
https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&state=STATE#wechat_redirect 若提示“该链接无法访问”,请检查参数是否填写错误,是否拥有scope参数对应的授权作用域权限。
如需要页面跳转则可通过设置此处的 redirect_uri 来实现。
下面是通过回调的方式,让腾讯回调我们自己的控制器来获取所需要的信息。
通过code获取openid
@RequestMapping(value="/authRedirect.do",method = RequestMethod.GET) public Object authRedirect(@RequestParam(required = true) String state,Model model, @RequestParam(required = true) String code, HttpServletRequest request, HttpServletResponse response, RedirectAttributes redirectAttributes) throws Exception { String redirectUrl=""; logger.info("---------------微信授权成功回调开始--------------"); try { if (StringUtils.isNotBlank(code)) { String oauth_access_token_url = this.wxPropertiesBean.getProperties("wxpt").getProperty("oauth_access_token_url"); String sysytemAppid = (String) wxPropertiesBean.getProperties("system").get("system_appid"); Map config = this.wxMpService.queryWxAccountByCode(sysytemAppid); Map<String,Object> urlParams = new HashMap<String,Object>(); urlParams.put("appid", config.get("account_appid")); urlParams.put("secret", config.get("account_appsecret")); urlParams.put("code", code); String url = HttpUtil.getUrl(oauth_access_token_url, urlParams); WxApiResult apiResult = HttpUtil.httpsRequest(url, "GET", null); Map result = HttpUtil.getCommonResult(apiResult); String openid = (String) result.get("openid"); if(StringUtils.isNotBlank(openid)){ request.getSession().setAttribute("openid", openid); } } //查询自定义菜单信息 Map channel = this.wxMpService.queryWxChannelByCode(state); String relaPath = this.wxPropertiesBean.getProperties("system").getProperty(state); if(channel!=null&&channel.get("link_url")!=null){ redirectUrl = (String) channel.get("link_url"); }else if(relaPath!=null){ String protocol = (String) config.get("protocol"); String domain = (String) config.get("domain"); redirectUrl = protocol + domain + relaPath; }else{ String protocol = (String) config.get("protocol"); String domain = (String) config.get("domain"); redirectUrl = protocol + domain; } if(redirectUrl.indexOf("?") > -1) { redirectUrl = redirectUrl +"&v="+Math.random(); }else{ redirectUrl = redirectUrl +"?v="+Math.random(); } } } catch (Exception e) { logger.error(e.getMessage()); throw new Exception("系统无法完成请求,错误信息:"+e.getMessage()); } return "redirect:" + redirectUrl; }
/** * 发送https请求 * @param requestUrl 请求地址 * @param requestMethod 请求方式(GET、POST) * @param outputStr 提交的数据 * @return JSONObject(通过JSONObject.get(key)的方式获取json对象的属性值) */ public static WxApiResult httpsRequest(String requestUrl, String requestMethod, String outputStr) { WxApiResult message = new WxApiResult(); try { TrustManager[] tm = { new MyX509TrustManager() }; SSLContext sslContext = SSLContext.getInstance("SSL", "SunJSSE"); sslContext.init(null, tm, new java.security.SecureRandom()); SSLSocketFactory ssf = sslContext.getSocketFactory(); URL url = new URL(requestUrl); HttpsURLConnection conn = (HttpsURLConnection) url.openConnection(); conn.setSSLSocketFactory(ssf); conn.setDoOutput(true); conn.setDoInput(true); conn.setUseCaches(false); conn.setRequestMethod(requestMethod); if (null != outputStr) { OutputStream outputStream = conn.getOutputStream(); outputStream.write(outputStr.getBytes("UTF-8")); outputStream.close(); } InputStream inputStream = conn.getInputStream(); InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "utf-8"); BufferedReader bufferedReader = new BufferedReader(inputStreamReader); String str = null; StringBuffer buffer = new StringBuffer(); while ((str = bufferedReader.readLine()) != null) { buffer.append(str); } if(conn.getResponseCode()==conn.HTTP_OK){ message.setCode(0); message.setContent(buffer.toString()); }else{ message.setCode(-1); message.setContent("获取返回状态不对,返回状态为:"+conn.getResponseCode()); } // 释放资源 bufferedReader.close(); inputStreamReader.close(); inputStream.close(); inputStream = null; conn.disconnect(); } catch (ConnectException ce) { log.error("连接超时:{}", ce); message.setCode(-1); message.setContent(ce.getMessage()); } catch (Exception e) { log.error("https请求异常:{}", e); message.setCode(-1); message.setContent(e.getMessage()); } return message; }
如需获取用户信息只需通过上述代码中获取到的openid和access_token获取即可。
@Override public String getCustImg(HttpServletRequest request, Map<String, Object> param){ try{ String openid = (String) request.getSession().getAttribute("openid"); String oauth_access_token_url = "https://api.weixin.qq.com/sns/userinfo"; String accessToken = wxTokenComp.getAccessToken(); Map urlParams = new HashMap(); urlParams.put("openid",openid); urlParams.put("access_token",accessToken); urlParams.put("lang","zh_CN"); String url = HttpUtil.getUrl(oauth_access_token_url, urlParams); WxApiResult apiResult = HttpUtil.httpsRequest(url, "GET", null); Map rstMap = HttpUtil.getCommonResult(apiResult); }catch(Exception e){ e.printStackTrace(); } return rstMap.get("headimgurl");
}