轉自:http://www.cuiyongzhi.com/post/78.htmlphp
好長時間沒有寫文章了,主要是最近的工做和生活上的事情比較多並且繁瑣,其實到如今我依然仍是感受有些迷茫,最後仍是決定靜下心來堅持一開始的選擇,繼續咱們的微信系列文章的後續更新,也但願在本身有時間的時候能把更多的內容呈現給你們,前面一系列的文章講述了不少微信開發相關的基礎知識點 【微信系列文章】,那麼從這一篇開始將講述微信較深一層次或者說在產品應用中時刻會用到的一些技術點,那麼下面就讓咱們進入正題吧,這一篇我要講述的是在微信服務號開發中經常使用到的微信網頁受權(OAuth2.0受權)獲取用戶基本信息!html
咱們來講下OAuth2.0受權的一些應用場景,例如:示例①但願用戶在點擊打開一個網頁的時候能夠獲取該用戶的微信暱稱,微信頭像等;示例②:當某個用戶在咱們會員系統中,但願用戶在直接打開網頁的時候展現用戶在咱們的會員系統中的積分狀況和消費信息等...,那麼這一系列的應用場景中咱們是如何在一個網頁中獲取微信用戶的相關信息的呢?這裏咱們就要用到微信網頁受權的功能來解決咱們遇到的難題了!前端
(一)首先在咱們開始應用OAuth2.0受權以前咱們先來了解一下它,看他自己的實現流程和機制!java
①關於網頁受權回調域名的說明web
在微信公衆號請求用戶網頁受權以前,開發者須要先到公衆平臺官網中的開發者中心頁配置受權回調域名。請注意,這裏填寫的是域名(是一個字符串),而不是URL,所以請勿加 http:// 等協議頭;json
受權回調域名配置規範爲全域名,好比須要網頁受權的域名爲: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鑑權api
若是公衆號登陸受權給了第三方開發者來進行管理,則沒必要作任何設置,由第三方代替公衆號實現網頁受權便可安全
②關於網頁受權的兩種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);
}
}
|