Java微信公眾平臺開發(十六)--微信網頁授權(OAuth2.0授權)獲取用戶基本信息
轉自:http://www.cuiyongzhi.com/post/78.html
好長時間沒有寫文章了,主要是最近的工作和生活上的事情比較多而且繁瑣,其實到現在我依然還是感覺有些迷茫,最後還是決定靜下心來堅持一開始的選擇,繼續我們的微信系列文章的後續更新,也希望在自己有時間的時候能把更多的內容呈現給大家,前面一系列的文章講述了很多微信開發相關的基礎知識點 【微信系列文章】,那麽從這一篇開始將講述微信較深一層次或者說在產品應用中時刻會用到的一些技術點,那麽下面就讓我們進入正題吧,這一篇我要講述的是在微信服務號開發中常用到的微信網頁授權(OAuth2.0授權)獲取用戶基本信息!
我們來說下OAuth2.0授權的一些應用場景,例如:示例①希望用戶在點擊打開一個網頁的時候可以獲取該用戶的微信昵稱,微信頭像等;示例②:當某個用戶在我們會員系統中,希望用戶在直接打開網頁的時候展示用戶在我們的會員系統中的積分情況和消費信息等...,那麽這一系列的應用場景中我們是如何在一個網頁中獲取微信用戶的相關信息的呢?這裏我們就要用到微信網頁授權的功能來解決我們遇到的難題了!
(一)首先在我們開始應用OAuth2.0授權之前我們先來了解一下它,看他本身的實現流程和機制!
①關於網頁授權回調域名的說明
-
在微信公眾號請求用戶網頁授權之前,開發者需要先到公眾平臺官網中的開發者中心頁配置授權回調域名。請註意,這裏填寫的是域名(是一個字符串),而不是URL,因此請勿加 http:// 等協議頭;
-
授權回調域名配置規範為全域名,比如需要網頁授權的域名為: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鑒權
-
如果公眾號登錄授權給了第三方開發者來進行管理,則不必做任何設置,由第三方代替公眾號實現網頁授權即可
②關於網頁授權的兩種scope的區別說明
-
以snsapi_base為scope發起的網頁授權,是用來獲取進入頁面的用戶的openid的,並且是靜默授權並自動跳轉到回調頁的。用戶感知的就是直接進入了回調頁(往往是業務頁面)
-
以snsapi_userinfo為scope發起的網頁授權,是用來獲取用戶的基本信息的。但這種授權需要用戶手動同意,並且由於用戶同意過,所以無須關註,就可在授權後獲取該用戶的基本信息。
-
用戶管理類接口中的“獲取用戶基本信息接口”,是在用戶和公眾號產生消息交互或關註後事件推送後,才能根據用戶OpenID來獲取用戶基本信息。這個接口,包括其他微信接口,都是需要該用戶(即openid)關註了公眾號後,才能調用成功的。
③關於網頁授權access_token和普通access_token的區別(參見:微信開發中的token獲取)
-
微信網頁授權是通過OAuth2.0機制實現的,在用戶授權給公眾號後,公眾號可以獲取到一個網頁授權特有的接口調用憑證(網頁授權access_token),通過網頁授權access_token可以進行授權後接口調用,如獲取用戶基本信息;
-
其他微信接口,需要通過基礎支持中的“獲取access_token”接口來獲取到的普通access_token調用。
④關於UnionID機制(參見:開發中微信公眾平臺/開放平臺/商戶平臺的關聯)
-
請註意,網頁授權獲取用戶基本信息也遵循UnionID機制。即如果開發者有在多個公眾號,或在公眾號、移動應用之間統一用戶帳號的需求,需要前往微信開放平臺(open.weixin.qq.com)綁定公眾號後,才可利用UnionID機制來滿足上述需求。
-
UnionID機制的作用說明:如果開發者擁有多個移動應用、網站應用和公眾帳號,可通過獲取用戶基本信息中的unionid來區分用戶的唯一性,因為同一用戶,對同一個微信開放平臺下的不同應用(移動應用、網站應用和公眾帳號),unionid是相同的。
⑤關於特殊場景下的靜默授權
-
上面已經提到,對於以snsapi_base為scope的網頁授權,就靜默授權的,用戶無感知;
-
對於已關註公眾號的用戶,如果用戶從公眾號的會話或者自定義菜單進入本公眾號的網頁授權頁,即使是scope為snsapi_userinfo,也是靜默授權,用戶無感知。
(二)OAuth2.0授權獲取用戶微信信息的具體實現
在整個的授權獲取用戶信息的流程中可以分為以下幾步:
-
開發前的授權域名配置
-
引導用戶進入授權頁面同意授權,獲取code
-
通過code換取網頁授權access_token(與基礎支持中的access_token不同)並通過網頁授權access_token和openid獲取用戶基本信息(支持UnionID機制)
①開發前的授權域名配置
在進行代碼實現之前我們需要配置安全授權域名,具體安全域名的配置目錄:【微信公眾平臺】——>【接口權限】——>【網頁授權獲取用戶基本信息】,簡單如下圖:
②引導用戶進入授權頁面同意授權,獲取code
這一步在整個的網頁授權過程中是非常重要的一步,因為只有引導用戶授權獲取到code才能開始後面信息的獲取,在這裏需要註意的是我們在配置授權鏈接中的redirect_uri必須是我們在第一步中配置的安全域名,參考連接如下:
1 2 3 4 |
Scope為snsapi_base
Scope為snsapi_userinfo
https: //open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=redirect_uri&response_type=code&scope=snsapi_userinfo&state=2#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重定向時候,必須帶此參數 |
註:code作為換取access_token的票據,每次用戶授權帶上的code將不一樣,code只能使用一次,5分鐘未被使用自動過期!
因為oauth2.0授權機制是在騰訊機器會將最終的url帶上code中轉到我們所配置的redirect_uri上,所以在我們的服務端我們需要在Controller加入一個weiXinOauth方法用於接收騰訊服務器中轉過來的參數code和state,簡單實現如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
/**
* @Description: 微信授權登錄
* @param @param request
* @param @param response
* @param @param code
* @param @param state
* @author dapengniao
* @date 2016年4月26日 上午9:40:18
*/
@RequestMapping ( "weixinOauth" )
public void weiXinOauth(HttpServletRequest request,
HttpServletResponse response,
@RequestParam (value = "code" , required = true ) String code,
@RequestParam (value = "state" , required = true ) String state) {
System.out.println( "Code=============" +code+ "==========state=======" +state);
}
|
此時到這裏我們對code的獲取就完成了,下面繼續下一步操作;
③通過code換取網頁授權access_token(與基礎支持中的access_token不同)並通過網頁授權access_token和openid獲取用戶基本信息(支持UnionID機制)
在前面我們通過方法獲取到了在授權中需要用到的code,那麽我們接下來要做的就是通過code獲取token+openid,這裏如果我們采用的是snsapi_userinfo的方式授權的話,那麽後面我們可以通過token+openid獲取用戶信息了,在這裏我寫了一個實用的方法OauthCode_GetUseInfo來實現這些步驟,簡單代碼如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 |
package com.cuiyongzhi.wechat.common;
import java.util.HashMap;
import com.cuiyongzhi.web.util.GlobalConstants;
import com.cuiyongzhi.wechat.util.HttpUtils;
import net.sf.json.JSONObject;
/**
* ClassName: OauthCode_GetUseInfo
* @Description: 用戶oauth2.0授權登錄 通過code獲取用戶真實信息
* @author dapengniao
* @date 2016年4月26日 上午9:54:55
*/
public class OauthCode_GetUseInfo {
private String openid;
private String access_token;
private String code;
private String unionid;
private HashMap<String, String> params = new HashMap<String, String>();
public OauthCode_GetUseInfo(String code) {
this .code = code;
params.put( "appid" , GlobalConstants.getInterfaceUrl( "appid" ));
params.put( "secret" , GlobalConstants.getInterfaceUrl( "AppSecret" ));
}
/**
*
* @param @return hashmap {subscribe是否關註 0沒有關註,1關註 unionid openid nickname昵稱
* sex性別 province省份 city城市 headimgurl圖像地址}
* @param @throws Exception
* @author dapengniao
* @date 2016年4月26日 上午9:54:55
*/
public HashMap<String, String> getUserInfo() throws Exception {
// 將用戶信息獲取拼裝成map
// 通過code獲取access_token,openid,unionid
params.put( "code" , code);
params.put( "grant_type" , "authorization_code" );
String tokenrs = HttpUtils.sendGet(
GlobalConstants.getInterfaceUrl( "OauthCodeUrl" ), params);
System.out.println( "tokenrs======================" +tokenrs);
access_token = JSONObject.fromObject(tokenrs).getString( "access_token" );
openid = JSONObject.fromObject(tokenrs).getString( "openid" );
unionid = JSONObject.fromObject(tokenrs).getString( "unionid" );
// 通過用戶openid信息獲取用戶詳細信息
params.clear();
params.put( "access_token" , access_token);
params.put( "openid" , openid);
params.put( "lang" , "zh_CN" );
String useinfors = HttpUtils.sendGet(
GlobalConstants.getInterfaceUrl( "OauthInfoUrl" ), params);
// 通過用戶的openid判斷用戶是否關註公眾賬號
params.clear();
params.put( "access_token" , GlobalConstants.getInterfaceUrl( "access_token" )
);
params.put( "openid" , openid);
params.put( "lang" , "zh_CN" );
String subscribers = "" ;
subscribers = HttpUtils.sendGet(
GlobalConstants.getInterfaceUrl( "SubscribeUrl" ), params);
// 將用戶信息獲取拼裝成map
System.out.println(subscribers);
params.clear();
params.put( "subscribe" ,
JSONObject.fromObject(subscribers).getString( "subscribe" ));
params.put( "unionid" , unionid);
params.put( "openid" , openid);
params.put( "nickname" ,
JSONObject.fromObject(useinfors).getString( "nickname" ));
params.put( "sex" , JSONObject.fromObject(useinfors).getString( "sex" ));
params.put( "province" ,
JSONObject.fromObject(useinfors).getString( "province" ));
params.put( "city" , JSONObject.fromObject(useinfors).getString( "city" ));
params.put( "headimgurl" ,
JSONObject.fromObject(useinfors).getString( "headimgurl" ));
return params;
}
/**
* @Description: 通過openid獲取用戶信息
* @param @param openid
* @param @return
* @param @throws Exception
* @author dapengniao
* @date 2016年4月26日 上午9:53:40
*/
public static HashMap<String, String> Openid_userinfo(String openid)
throws Exception {
HashMap<String, String> params = new HashMap<String, String>();
params.put( "access_token" ,
GlobalConstants.getInterfaceUrl( "access_token" ));
params.put( "openid" , openid);
params.put( "lang" , "zh_CN" );
String subscribers = HttpUtils.sendGet(
GlobalConstants.getInterfaceUrl( "SubscribeUrl" ), params);
params.clear();
params.put( "nickname" ,
JSONObject.fromObject(subscribers).getString( "nickname" ));
params.put( "headimgurl" ,
JSONObject.fromObject(subscribers).getString( "headimgurl" ));
params.put( "sex" , JSONObject.fromObject(subscribers).getString( "sex" ));
return params;
}
@SuppressWarnings ( "unused" )
private static String create_timestamp() {
return Long.toString(System.currentTimeMillis() / 1000 );
}
}
|
通過方法我們只需要傳入code,就可以將個人的用戶信息以Map的形式返回,用於我們在前端的使用,在這裏我們將Controller的代碼做簡單的修改,用於對OauthCode_GetUseInfo中方法對用戶信息的獲取,如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
/**
* @Description: 微信授權登錄
* @param @param request
* @param @param response
* @param @param code
* @param @param state
* @author dapengniao
* @date 2016年4月26日 上午9:40:18
*/
@RequestMapping ( "weixinOauth" )
public void weiXinOauth(HttpServletRequest request,
HttpServletResponse response,
@RequestParam (value = "code" , required = true ) String code,
@RequestParam (value = "state" , required = true ) String state) {
System.out.println( "Code=============" +code+ "==========state=======" + state);
try {
// 用code取得微信用戶的基本信息
OauthCode_GetUseInfo weixin = new OauthCode_GetUseInfo(code);
Map<String, String> wmap = weixin.getUserInfo();
System.out.println( "用戶昵稱================================="
+ wmap.get( "nickname" ));
} catch (Exception e) {
logger.error(e.toString(), e);
}
}
|
Java微信公眾平臺開發(十六)--微信網頁授權(OAuth2.0授權)獲取用戶基本信息