实现微信打开网页时,获取当前微信用户信息(Java)(基于snsapi_userinfo方式)
第一部分:微信授權獲取基本信息的介紹
?
我們首先來看看官方的文檔怎么說:
如果用戶在微信客戶端中訪問第三方網頁,公眾號可以通過微信網頁授權機制,來獲取用戶基本信息,進而實現業務邏輯。
關于網頁授權回調域名的說明
1、在微信公眾號請求用戶網頁授權之前,開發者需要先到公眾平臺官網中的開發者中心頁配置授權回調域名。請注意,這里填寫的是域名(是一個字符串),而不是URL,因此請勿加http://等協議頭; 2、授權回調域名配置規范為全域名,比如需要網頁授權的域名為:www.qq.com,配置以后此域名下面的頁面http://www.qq.com/music.html 、 http://www.qq.com/login.html 都可以進行OAuth2.0鑒權。但http://pay.qq.com 、 http://music.qq.com 、 http://qq.com無法進行OAuth2.0鑒權 3、如果公眾號登錄授權給了第三方開發者來進行管理,則不必做任何設置,由第三方代替公眾號實現網頁授權即可關于網頁授權的兩種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調用。關于UnionID機制
1、請注意,網頁授權獲取用戶基本信息也遵循UnionID機制。即如果開發者有在多個公眾號,或在公眾號、移動應用之間統一用戶帳號的需求,需要前往微信開放平臺(open.weixin.qq.com)綁定公眾號后,才可利用UnionID機制來滿足上述需求。 2、UnionID機制的作用說明:如果開發者擁有多個移動應用、網站應用和公眾帳號,可通過獲取用戶基本信息中的unionid來區分用戶的唯一性,因為同一用戶,對同一個微信開放平臺下的不同應用(移動應用、網站應用和公眾帳號),unionid是相同的。關于特殊場景下的靜默授權
1、上面已經提到,對于以snsapi_base為scope的網頁授權,就靜默授權的,用戶無感知; 2、對于已關注公眾號的用戶,如果用戶從公眾號的會話或者自定義菜單進入本公眾號的網頁授權頁,即使是scope為snsapi_userinfo,也是靜默授權,用戶無感知。?
具體而言,網頁授權流程分為四步:
1、引導用戶進入授權頁面同意授權,獲取code 2、通過code換取網頁授權access_token(與基礎支持中的access_token不同) 3、如果需要,開發者可以刷新網頁授權access_token,避免過期 4、通過網頁授權access_token和openid獲取用戶基本信息(支持UnionID機制)?
?第二部分:實現微信網頁授權的詳細方法
?
下面,我們來按照這個步驟來實現這個功能:
?
第一步:用戶同意授權,獲取code
?
?
在確保微信公眾賬號擁有授權作用域(scope參數)的權限的前提下(服務號獲得高級接口后,默認擁有scope參數中的snsapi_base和snsapi_userinfo),引導關注者打開如下頁面
?
參考鏈接(請在微信客戶端中打開此鏈接體驗)Scope為snsapi_basehttps://open.weixin.qq.com/connect/oauth2/authorize?appid=wx520c15f417810387&redirect_uri=https%3A%2F%2Fchong.qq.com%2Fphp%2Findex.php%3Fd%3D%26c%3DwxAdapter%26m%3DmobileDeal%26showwxpaytitle%3D1%26vb2ctag%3D4_2030_5_1194_60&response_type=code&scope=snsapi_base&state=123#wechat_redirectScope為snsapi_userinfohttps://open.weixin.qq.com/connect/oauth2/authorize?appid=wxf0e81c3bee622d60&redirect_uri=http%3A%2F%2Fnba.bluewebgame.com%2Foauth_response.php&response_type=code&scope=snsapi_userinfo&state=STATE#wechat_redirect?
?
?
參數說明
?
| appid | 是 | 公眾號的唯一標識 |
| redirect_uri | 是 | 授權后重定向的回調鏈接地址,請使用urlencode對鏈接進行處理 |
| response_type | 是 | 返回類型,請填寫code |
| scope | 是 | 應用授權作用域,snsapi_base (不彈出授權頁面,直接跳轉,只能獲取用戶openid),snsapi_userinfo (彈出授權頁面,可通過openid拿到昵稱、性別、所在地。并且,即使在未關注的情況下,只要用戶授權,也能獲取其信息) |
| state | 否 | 重定向后會帶上state參數,開發者可以填寫a-zA-Z0-9的參數值,最多128字節 |
| #wechat_redirect | 是 | 無論直接打開還是做頁面302重定向時候,必須帶此參數 |
下圖為scope等于snsapi_userinfo時的授權頁面:
?
?
用戶同意授權后
如果用戶同意授權,頁面將跳轉至 redirect_uri/?code=CODE&state=STATE。若用戶禁止授權,則重定向后不會帶上code參數,僅會帶上state參數redirect_uri?state=STATE
?
code說明 : code作為換取access_token的票據,每次用戶授權帶上的code將不一樣,code只能使用一次,5分鐘未被使用自動過期。?
?
溫馨提醒:以下的省略了搭建環境和導入jar的過程,以下的方法提供參考。如果需要的話,需要看下前面的系列文章。
?
?
我們首先創建一些需要用到的pojo :
?
1.?通過網頁授權獲取的用戶信息
?
用戶信息類:SNSUserInfo類
?
package com.souvc.weixin.pojo;import java.util.List;/** * 類名: SNSUserInfo </br> * 描述: 通過網頁授權獲取的用戶信息 </br> * 開發人員: souvc </br> * 創建時間: 2015-11-27 </br> * 發布版本:V1.0 </br>*/ public class SNSUserInfo {// 用戶標識private String openId;// 用戶昵稱private String nickname;// 性別(1是男性,2是女性,0是未知)private int sex;// 國家private String country;// 省份private String province;// 城市private String city;// 用戶頭像鏈接private String headImgUrl;// 用戶特權信息private List<String> privilegeList;public String getOpenId() {return openId;}public void setOpenId(String openId) {this.openId = openId;}public String getNickname() {return nickname;}public void setNickname(String nickname) {this.nickname = nickname;}public int getSex() {return sex;}public void setSex(int sex) {this.sex = sex;}public String getCountry() {return country;}public void setCountry(String country) {this.country = country;}public String getProvince() {return province;}public void setProvince(String province) {this.province = province;}public String getCity() {return city;}public void setCity(String city) {this.city = city;}public String getHeadImgUrl() {return headImgUrl;}public void setHeadImgUrl(String headImgUrl) {this.headImgUrl = headImgUrl;}public List<String> getPrivilegeList() {return privilegeList;}public void setPrivilegeList(List<String> privilegeList) {this.privilegeList = privilegeList;} }View Code
?
?
2.??憑證實體類
?
package com.souvc.weixin.pojo;/** * 類名: Token </br> * 描述: 憑證 </br> * 開發人員: souvc </br> * 創建時間: 2015-11-27 </br> * 發布版本:V1.0 </br>*/ public class Token {// 接口訪問憑證private String accessToken;// 憑證有效期,單位:秒private int expiresIn;public String getAccessToken() {return accessToken;}public void setAccessToken(String accessToken) {this.accessToken = accessToken;}public int getExpiresIn() {return expiresIn;}public void setExpiresIn(int expiresIn) {this.expiresIn = expiresIn;} }View Code
?
?
3.?網頁授權信息 WeixinOauth2Token類
?
package com.souvc.weixin.pojo;/** * 類名: WeixinOauth2Token </br> * 描述: 網頁授權信息 </br> * 開發人員: souvc </br> * 創建時間: 2015-11-27 </br> * 發布版本:V1.0 </br>*/ public class WeixinOauth2Token {// 網頁授權接口調用憑證private String accessToken;// 憑證有效時長private int expiresIn;// 用于刷新憑證private String refreshToken;// 用戶標識private String openId;// 用戶授權作用域private String scope;public String getAccessToken() {return accessToken;}public void setAccessToken(String accessToken) {this.accessToken = accessToken;}public int getExpiresIn() {return expiresIn;}public void setExpiresIn(int expiresIn) {this.expiresIn = expiresIn;}public String getRefreshToken() {return refreshToken;}public void setRefreshToken(String refreshToken) {this.refreshToken = refreshToken;}public String getOpenId() {return openId;}public void setOpenId(String openId) {this.openId = openId;}public String getScope() {return scope;}public void setScope(String scope) {this.scope = scope;} }View Code
?
?
4.??微信用戶的基本信息WeixinUserInfo類
?
?
package com.souvc.weixin.pojo;/** * 類名: WeixinUserInfo </br> * 描述: 微信用戶的基本信息 </br> * 開發人員: souvc </br> * 創建時間: 2015-11-27 </br> * 發布版本:V1.0 </br>*/ public class WeixinUserInfo {// 用戶的標識private String openId;// 關注狀態(1是關注,0是未關注),未關注時獲取不到其余信息private int subscribe;// 用戶關注時間,為時間戳。如果用戶曾多次關注,則取最后關注時間private String subscribeTime;// 昵稱private String nickname;// 用戶的性別(1是男性,2是女性,0是未知)private int sex;// 用戶所在國家private String country;// 用戶所在省份private String province;// 用戶所在城市private String city;// 用戶的語言,簡體中文為zh_CNprivate String language;// 用戶頭像private String headImgUrl;public String getOpenId() {return openId;}public void setOpenId(String openId) {this.openId = openId;}public int getSubscribe() {return subscribe;}public void setSubscribe(int subscribe) {this.subscribe = subscribe;}public String getSubscribeTime() {return subscribeTime;}public void setSubscribeTime(String subscribeTime) {this.subscribeTime = subscribeTime;}public String getNickname() {return nickname;}public void setNickname(String nickname) {this.nickname = nickname;}public int getSex() {return sex;}public void setSex(int sex) {this.sex = sex;}public String getCountry() {return country;}public void setCountry(String country) {this.country = country;}public String getProvince() {return province;}public void setProvince(String province) {this.province = province;}public String getCity() {return city;}public void setCity(String city) {this.city = city;}public String getLanguage() {return language;}public void setLanguage(String language) {this.language = language;}public String getHeadImgUrl() {return headImgUrl;}public void setHeadImgUrl(String headImgUrl) {this.headImgUrl = headImgUrl;} }View Code
?
?
5. 封裝AdvancedUtil來實現以下方法 。
?
但是如何獲取到token值呢?
?
/*** 獲取網頁授權憑證* * @param appId 公眾賬號的唯一標識* @param appSecret 公眾賬號的密鑰* @param code* @return WeixinAouth2Token*/public static WeixinOauth2Token getOauth2AccessToken(String appId, String appSecret, String code) {WeixinOauth2Token wat = null;// 拼接請求地址String requestUrl = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code";requestUrl = requestUrl.replace("APPID", appId);requestUrl = requestUrl.replace("SECRET", appSecret);requestUrl = requestUrl.replace("CODE", code);// 獲取網頁授權憑證JSONObject jsonObject = CommonUtil.httpsRequest(requestUrl, "GET", null);if (null != jsonObject) {try {wat = new WeixinOauth2Token();wat.setAccessToken(jsonObject.getString("access_token"));wat.setExpiresIn(jsonObject.getInt("expires_in"));wat.setRefreshToken(jsonObject.getString("refresh_token"));wat.setOpenId(jsonObject.getString("openid"));wat.setScope(jsonObject.getString("scope"));} catch (Exception e) {wat = null;int errorCode = jsonObject.getInt("errcode");String errorMsg = jsonObject.getString("errmsg");log.error("獲取網頁授權憑證失敗 errcode:{} errmsg:{}", errorCode, errorMsg);}}return wat;}View Code
?
?
?獲取用戶信息:
?
?
/*** 通過網頁授權獲取用戶信息* * @param accessToken 網頁授權接口調用憑證* @param openId 用戶標識* @return SNSUserInfo*/@SuppressWarnings( { "deprecation", "unchecked" })public static SNSUserInfo getSNSUserInfo(String accessToken, String openId) {SNSUserInfo snsUserInfo = null;// 拼接請求地址String requestUrl = "https://api.weixin.qq.com/sns/userinfo?access_token=ACCESS_TOKEN&openid=OPENID";requestUrl = requestUrl.replace("ACCESS_TOKEN", accessToken).replace("OPENID", openId);// 通過網頁授權獲取用戶信息JSONObject jsonObject = CommonUtil.httpsRequest(requestUrl, "GET", null);if (null != jsonObject) {try {snsUserInfo = new SNSUserInfo();// 用戶的標識snsUserInfo.setOpenId(jsonObject.getString("openid"));// 昵稱snsUserInfo.setNickname(jsonObject.getString("nickname"));// 性別(1是男性,2是女性,0是未知)snsUserInfo.setSex(jsonObject.getInt("sex"));// 用戶所在國家snsUserInfo.setCountry(jsonObject.getString("country"));// 用戶所在省份snsUserInfo.setProvince(jsonObject.getString("province"));// 用戶所在城市snsUserInfo.setCity(jsonObject.getString("city"));// 用戶頭像snsUserInfo.setHeadImgUrl(jsonObject.getString("headimgurl"));// 用戶特權信息snsUserInfo.setPrivilegeList(JSONArray.toList(jsonObject.getJSONArray("privilege"), List.class));} catch (Exception e) {snsUserInfo = null;int errorCode = jsonObject.getInt("errcode");String errorMsg = jsonObject.getString("errmsg");log.error("獲取用戶信息失敗 errcode:{} errmsg:{}", errorCode, errorMsg);}}return snsUserInfo;}View Code
?
?
?6. 封裝https請求類 CommonUtil 類。
?
?
上面我們用到了一個支持發送https請求的工具:
?
package com.souvc.weixin.util;import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import javax.net.ssl.X509TrustManager;/** * 類名: MyX509TrustManager </br> * 描述:信任管理器 </br> * 開發人員: souvc </br> * 創建時間: 2015-11-27 </br> * 發布版本:V1.0 </br>*/ public class MyX509TrustManager implements X509TrustManager {// 檢查客戶端證書public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {}// 檢查服務器端證書public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {}// 返回受信任的X509證書數組public X509Certificate[] getAcceptedIssuers() {return null;} }View Code
?
?
?
/*** 發送https請求* * @param requestUrl 請求地址* @param requestMethod 請求方式(GET、POST)* @param outputStr 提交的數據* @return JSONObject(通過JSONObject.get(key)的方式獲取json對象的屬性值)*/public static JSONObject httpsRequest(String requestUrl, String requestMethod, String outputStr) {JSONObject jsonObject = null;try {// 創建SSLContext對象,并使用我們指定的信任管理器初始化TrustManager[] tm = { new MyX509TrustManager() };SSLContext sslContext = SSLContext.getInstance("SSL", "SunJSSE");sslContext.init(null, tm, new java.security.SecureRandom());// 從上述SSLContext對象中得到SSLSocketFactory對象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);// 設置請求方式(GET/POST)conn.setRequestMethod(requestMethod);// 當outputStr不為null時向輸出流寫數據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);}// 釋放資源bufferedReader.close();inputStreamReader.close();inputStream.close();inputStream = null;conn.disconnect();jsonObject = JSONObject.fromObject(buffer.toString());} catch (ConnectException ce) {log.error("連接超時:{}", ce);} catch (Exception e) {log.error("https請求異常:{}", e);}return jsonObject;}View Code
?
?
?
二、寫授權類:
?
注意替換成自己的appid 和 密鑰
?
?
package com.souvc.weixin.servlet;import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse;import com.souvc.weixin.pojo.SNSUserInfo; import com.souvc.weixin.pojo.WeixinOauth2Token; import com.souvc.weixin.util.AdvancedUtil;/** * 類名: OAuthServlet </br> * 描述: 授權后的回調請求處理 </br> * 開發人員: souvc </br> * 創建時間: 2015-11-27 </br> * 發布版本:V1.0 </br>*/ public class OAuthServlet extends HttpServlet {private static final long serialVersionUID = -1847238807216447030L;public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {request.setCharacterEncoding("utf-8");response.setCharacterEncoding("utf-8");// 用戶同意授權后,能獲取到codeString code = request.getParameter("code");String state = request.getParameter("state");// 用戶同意授權if (!"authdeny".equals(code)) {// 獲取網頁授權access_tokenWeixinOauth2Token weixinOauth2Token = AdvancedUtil.getOauth2AccessToken("wxe34a90ac7bxxcab85c", "1207d566090a8344xxxd6224c02c", code);// 網頁授權接口訪問憑證String accessToken = weixinOauth2Token.getAccessToken();// 用戶標識String openId = weixinOauth2Token.getOpenId();// 獲取用戶信息SNSUserInfo snsUserInfo = AdvancedUtil.getSNSUserInfo(accessToken, openId);// 設置要傳遞的參數request.setAttribute("snsUserInfo", snsUserInfo);request.setAttribute("state", state);}// 跳轉到index.jsprequest.getRequestDispatcher("index.jsp").forward(request, response);} }View Code
?
?
?
三、授權后,顯示信息的頁面
?
?
<%@ page language="java" pageEncoding="utf-8"%> <%@ page import="com.souvc.weixin.pojo.SNSUserInfo,java.lang.*"%> <html> <head><title>OAuth2.0網頁授權</title><meta name="viewport" content="width=device-width,user-scalable=0"><style type="text/css">*{margin:0; padding:0}table{border:1px dashed #B9B9DD;font-size:12pt}td{border:1px dashed #B9B9DD;word-break:break-all; word-wrap:break-word;}</style> </head> <body><% // 獲取由OAuthServlet中傳入的參數SNSUserInfo user = (SNSUserInfo)request.getAttribute("snsUserInfo"); String state=request.getAttribute("state").toString();if(null != user) {%><table width="100%" cellspacing="0" cellpadding="0"><tr><td width="20%">屬性</td><td width="80%">值</td></tr><tr><td>OpenID</td><td><%=user.getOpenId()%></td></tr><tr><td>昵稱</td><td><%=user.getNickname()%></td></tr><tr><td>性別</td><td><%=user.getSex()%></td></tr><tr><td>國家</td><td><%=user.getCountry()%></td></tr><tr><td>省份</td><td><%=user.getProvince()%></td></tr><tr><td>城市</td><td><%=user.getCity()%></td></tr><tr><td>頭像</td><td><%=user.getHeadImgUrl()%></td></tr><tr><td>特權</td><td><%=user.getPrivilegeList()%></td></tr><tr><td>state:</td><td><%=state%></td></tr></table><%}else out.print("用戶不同意授權,未獲取到用戶信息!");%> </body> </html>View Code
?
?
?
四、寫請求的路徑
?
?
<?xml version="1.0" encoding="UTF-8"?> <web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"><!--<servlet><servlet-name>coreServlet</servlet-name><servlet-class>com.souvc.weixin.servlet.CoreServlet</servlet-class></servlet>--> <!-- /coreServlet用于指定該Servlet的訪問路徑 <servlet-mapping> <servlet-name>coreServlet</servlet-name> <url-pattern>/coreServlet</url-pattern> </servlet-mapping>--><servlet><servlet-name>oauthServlet</servlet-name><servlet-class>com.souvc.weixin.servlet.OAuthServlet</servlet-class></servlet><servlet-mapping><servlet-name>oauthServlet</servlet-name><url-pattern>/oauthServlet</url-pattern></servlet-mapping><welcome-file-list><welcome-file>index.jsp</welcome-file></welcome-file-list> </web-app>View Code
?
?
?
五、替換官方的鏈接成我們的方法路徑:
?
官方的請求鏈接: https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&state=STATE#wechat_redirect ? ?需要修改的地方:
?
(1)替換自己的AppID
(2)將redirect_url換成自己的授權請求鏈接URL。注意這個連接需要經過UTF-8編碼。
(3)需要修改scope。需要彈出頁面則要修改為snsapi_userinfo 。
?
?
scope參數的解釋:
1、以snsapi_base為scope發起的網頁授權,是用來獲取進入頁面的用戶的openid的,并且是靜默授權并自動跳轉到回調頁的。用戶感知的就是直接進入了回調頁(往往是業務頁面)
2、以snsapi_userinfo為scope發起的網頁授權,是用來獲取用戶的基本信息的。但這種授權需要用戶手動同意,并且由于用戶同意過,所以無須關注,就可在授權后獲取該用戶的基本信息。?
?
?
?
/*** URL編碼(utf-8)* * @param source* @return*/public static String urlEncodeUTF8(String source) {String result = source;try {result = java.net.URLEncoder.encode(source, "utf-8");} catch (UnsupportedEncodingException e) {e.printStackTrace();}return result;}View Code
?
?
package com.souvc.weixin.util;public class TestURL {/** * 方法名:main</br>* 詳述:生成URL編碼 </br>* 開發人員:souvc </br>* 創建時間:2016-1-4 </br>* @param args 說明返回值含義* @throws 說明發生此異常的條件*/public static void main(String[] args) {String source="http://chiyan.duapp.com/oauthServlet";System.out.println(CommonUtil.urlEncodeUTF8(source));}}View Code
?
?
也可以直接在線url編碼:?http://tool.chinaz.com/Tools/URLEncode.aspx
?
?
六、復制上面替換好的鏈接,然后丟進瀏覽器,然后用微信來掃一掃。會出現以下的效果:
?
?
?
?
?
說明,恭喜你,我們獲取到了用戶的基本信息。
?
轉載:https://www.cnblogs.com/liuhongfeng/p/5099149.html
官方開發文檔:https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140842
?
?
總結
以上是生活随笔為你收集整理的实现微信打开网页时,获取当前微信用户信息(Java)(基于snsapi_userinfo方式)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: ensp启动路由 40错误-已解决
- 下一篇: 可编辑的下拉选单