若是用戶在微信客戶端中訪問第三方網頁,公衆號能夠經過微信網頁受權機制,來獲取用戶基本信息,進而實現業務邏輯。json
關於網頁受權的兩種scope的區別說明api
一、以snsapi_base爲scope發起的網頁受權,是用來獲取進入頁面的用戶的openid的,而且是靜默受權並自動跳轉到回調頁的。用戶感知的就是直接進入了回調頁(每每是業務頁面)微信
二、以snsapi_userinfo爲scope發起的網頁受權,是用來獲取用戶的基本信息的。但這種受權須要用戶手動贊成,而且因爲用戶贊成過,因此無須關注,就可在受權後獲取該用戶的基本信息。 app
三、用戶管理類接口中的「獲取用戶基本信息接口」,是在用戶和公衆號產生消息交互或關注後事件推送後,才能根據用戶OpenID來獲取用戶基本信息。這個接口,包括其餘微信接口,都是須要該用戶(即openid)關注了公衆號後,才能調用成功的。 微信公衆平臺
關於網頁受權access_token和普通access_token的區別dom
一、微信網頁受權是經過OAuth2.0機制實現的,在用戶受權給公衆號後,公衆號能夠獲取到一個網頁受權特有的接口調用憑證(網頁受權access_token),經過網頁受權access_token能夠進行受權後接口調用,如獲取用戶基本信息; ide
二、其餘微信接口,須要經過基礎支持中的「獲取access_token」接口來獲取到的普通access_token調用。 ui
關於特殊場景下的靜默受權this
一、上面已經提到,對於以snsapi_base爲scope的網頁受權,就靜默受權的,用戶無感知;
二、對於已關注公衆號的用戶,若是用戶從公衆號的會話或者自定義菜單進入本公衆號的網頁受權頁,即便是scope爲snsapi_userinfo,也是靜默受權,用戶無感知。
具體而言,網頁受權流程分爲四步:
一、引導用戶進入受權頁面贊成受權,獲取code
二、經過code換取網頁受權access_token(與基礎支持中的access_token不一樣)
三、若是須要,開發者能夠刷新網頁受權access_token,避免過時
四、經過網頁受權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");
}